From 9c955521e7a652c850f67cd71e4fa4d5902f08f5 Mon Sep 17 00:00:00 2001 From: Vicente Date: Wed, 19 Jun 2013 23:29:10 +0200 Subject: [PATCH] usbmodeswitch: Updated to v.1.2.6 from shibby's branch. dnscrypto-proxy: Script to convert ntp to IP address. Shibby branch. --- release/src/router/others/Makefile | 4 + release/src/router/others/ntp2ip | 13 + release/src/router/others/switch3g | 8 +- release/src/router/usbmodeswitch/ChangeLog | 47 + release/src/router/usbmodeswitch/Makefile | 73 +- release/src/router/usbmodeswitch/README | 148 +- .../usbmodeswitch/data/40-usb_modeswitch.rules | 1477 +- release/src/router/usbmodeswitch/data/ChangeLog | 62 + release/src/router/usbmodeswitch/data/Makefile | 21 +- release/src/router/usbmodeswitch/data/README | 49 +- .../src/router/usbmodeswitch/data/gen-rules.tcl | 88 + .../usbmodeswitch/data/usb_modeswitch.d/03f0:002a | 6 - .../usbmodeswitch/data/usb_modeswitch.d/0408:ea25 | 6 + .../usbmodeswitch/data/usb_modeswitch.d/0408:f000 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/0421:060c | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/0421:0610 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/0421:0618 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/0421:061d | 7 + .../usbmodeswitch/data/usb_modeswitch.d/0421:0622 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/0421:0627 | 8 +- .../usbmodeswitch/data/usb_modeswitch.d/0421:062c | 7 + .../usbmodeswitch/data/usb_modeswitch.d/0421:0632 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/0421:0637 | 7 + .../data/usb_modeswitch.d/0471:1210:uMa=Philips | 6 - .../data/usb_modeswitch.d/0471:1210:uMa=Wisue | 6 - .../usbmodeswitch/data/usb_modeswitch.d/0471:1237 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/0482:024d | 5 - .../usbmodeswitch/data/usb_modeswitch.d/04bb:bccd | 7 + .../usbmodeswitch/data/usb_modeswitch.d/04cc:225c | 6 + .../usbmodeswitch/data/usb_modeswitch.d/04e8:680c | 7 + .../usbmodeswitch/data/usb_modeswitch.d/04e8:689a | 6 - .../usbmodeswitch/data/usb_modeswitch.d/04e8:f000 | 3 - .../{04e8:f000 => 04e8:f000:sMo=U209} | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/057c:84ff | 6 - .../usbmodeswitch/data/usb_modeswitch.d/05c6:0010 | 7 + .../data/usb_modeswitch.d/05c6:1000:sVe=GT | 7 +- .../data/usb_modeswitch.d/05c6:1000:sVe=Option | 7 +- .../data/usb_modeswitch.d/05c6:1000:uMa=AnyDATA | 7 - .../data/usb_modeswitch.d/05c6:1000:uMa=CELOT | 7 + .../data/usb_modeswitch.d/05c6:1000:uMa=DGT | 7 + .../data/usb_modeswitch.d/05c6:1000:uMa=Option | 7 +- .../data/usb_modeswitch.d/05c6:1000:uMa=SAMSUNG | 7 - .../data/usb_modeswitch.d/05c6:1000:uMa=SSE | 6 - .../usb_modeswitch.d/05c6:1000:uMa=StrongRising | 7 + .../data/usb_modeswitch.d/05c6:1000:uMa=Vertex | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/05c6:2000 | 5 +- .../usbmodeswitch/data/usb_modeswitch.d/05c6:2001 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/05c6:6503 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/05c6:f000 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/05c7:1000 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/072f:100d | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/07d1:a800 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/07d1:a804 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/0922:1001 | 11 + .../usbmodeswitch/data/usb_modeswitch.d/0930:0d46 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/0ace:2011 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/0ace:20ff | 6 - .../usbmodeswitch/data/usb_modeswitch.d/0af0:4007 | 8 + .../usbmodeswitch/data/usb_modeswitch.d/0af0:6711 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:6731 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:6751 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:6771 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:6791 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:6811 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:6911 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:6951 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:6971 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7011 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7031 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7051 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7071 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7111 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7211 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7251 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7271 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7301 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7311 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7361 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7381 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7401 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7501 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7601 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7701 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7706 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/0af0:7801 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7901 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7a01 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:7a05 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:8006 | 8 + .../usbmodeswitch/data/usb_modeswitch.d/0af0:8200 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:8201 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:8300 | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:8302 | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:8304 | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:8400 | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:8600 | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:8700 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/0af0:8800 | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:8900 | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:9000 | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:9200 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/0af0:c031 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:c100 | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:d001 | 8 + .../usbmodeswitch/data/usb_modeswitch.d/0af0:d013 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:d031 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:d033 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:d035 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:d055 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:d057 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:d058 | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:d155 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:d157 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:d255 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:d257 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0af0:d357 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0b3c:c700 | 5 - .../usbmodeswitch/data/usb_modeswitch.d/0b3c:f000 | 5 - .../usbmodeswitch/data/usb_modeswitch.d/0cf3:20ff | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/0d46:45a1 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/0d46:45a5 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/0df7:0800 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/0e8d:0002 | 7 + .../data/usb_modeswitch.d/0e8d:0002:uPr=MT | 7 + .../usbmodeswitch/data/usb_modeswitch.d/0e8d:7109 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/0fce:d0cf | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/0fce:d0e1 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/0fce:d103 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/0fd1:1000 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/1004:1000 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/1004:607f | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/1004:613a | 5 - .../usbmodeswitch/data/usb_modeswitch.d/1004:613f | 5 - .../data/usb_modeswitch.d/{19d2:0003 => 1004:614e} | 12 +- .../usbmodeswitch/data/usb_modeswitch.d/1004:6156 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1004:6190 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/1004:61aa | 6 + .../usbmodeswitch/data/usb_modeswitch.d/1004:61dd | 8 + .../usbmodeswitch/data/usb_modeswitch.d/1004:61e7 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1004:61eb | 8 + .../usbmodeswitch/data/usb_modeswitch.d/1004:6327 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1033:0035 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/106c:3b03 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/106c:3b05 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/106c:3b06 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/106c:3b11 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/106c:3b14 | 8 + .../usbmodeswitch/data/usb_modeswitch.d/1076:7f40 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/109b:f009 | 8 + .../usbmodeswitch/data/usb_modeswitch.d/1199:0fff | 23 +- .../usbmodeswitch/data/usb_modeswitch.d/1266:1000 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/12d1:1001 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:1003 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:1009 | 7 - .../usbmodeswitch/data/usb_modeswitch.d/12d1:101e | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:1030 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/12d1:1031 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:1414 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:1446 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:1449 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/12d1:14ad | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:14b5 | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:14b7 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:14ba | 7 + .../usbmodeswitch/data/usb_modeswitch.d/12d1:14c1 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:14c3 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/12d1:14c4 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/12d1:14c5 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:14d1 | 8 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:14fe | 6 - .../usbmodeswitch/data/usb_modeswitch.d/12d1:1505 | 11 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:151a | 7 + .../usbmodeswitch/data/usb_modeswitch.d/12d1:1520 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:1521 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:1523 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:1526 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/12d1:1553 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/12d1:1557 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:155b | 7 + .../usbmodeswitch/data/usb_modeswitch.d/12d1:1805 | 6 + .../usbmodeswitch/data/usb_modeswitch.d/12d1:1c0b | 8 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:1c1b | 8 + .../usbmodeswitch/data/usb_modeswitch.d/12d1:1c24 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/12d1:1d50 | 6 + .../usbmodeswitch/data/usb_modeswitch.d/12d1:1da1 | 14 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:1f01 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/12d1:1f03 | 8 + .../usbmodeswitch/data/usb_modeswitch.d/12d1:1f11 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/12d1:380b | 6 - .../usbmodeswitch/data/usb_modeswitch.d/1307:1169 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/1410:5010 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/1410:5020 | 11 +- .../usbmodeswitch/data/usb_modeswitch.d/1410:5023 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1410:5030 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/1410:5031 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/1410:5041 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/1410:5059 | 8 + .../usbmodeswitch/data/usb_modeswitch.d/1410:7001 | 5 + .../usbmodeswitch/data/usb_modeswitch.d/148e:a000 | 5 - .../usbmodeswitch/data/usb_modeswitch.d/148f:2578 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/15eb:7153 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/16d8:6281 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/16d8:6803 | 12 +- .../usbmodeswitch/data/usb_modeswitch.d/16d8:6804 | 6 + .../usbmodeswitch/data/usb_modeswitch.d/16d8:700a | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/16d8:700b | 6 - .../usbmodeswitch/data/usb_modeswitch.d/16d8:f000 | 7 +- .../data/usb_modeswitch.d/{19d2:0115 => 1726:1900} | 13 +- .../usbmodeswitch/data/usb_modeswitch.d/1726:f00e | 7 + .../usbmodeswitch/data/usb_modeswitch.d/198a:0003 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/198f:bccd | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:0003 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:0013 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:0026 | 8 +- .../data/usb_modeswitch.d/{19d2:0026 => 19d2:0031} | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:0040 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:0053 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:0083 | 7 - .../{19d2:0083 => 19d2:0083:uPr=WCDMA} | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:0101 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:0103 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:0110 | 7 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:0115 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:0120 | 12 + .../usbmodeswitch/data/usb_modeswitch.d/19d2:0146 | 8 + .../usbmodeswitch/data/usb_modeswitch.d/19d2:0149 | 6 - .../data/usb_modeswitch.d/{19d2:0083 => 19d2:0150} | 9 +- .../data/usb_modeswitch.d/{19d2:1224 => 19d2:0154} | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:0166 | 11 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:0169 | 8 + .../usbmodeswitch/data/usb_modeswitch.d/19d2:0266 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/19d2:0325 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/19d2:1001 | 29 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:1007 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:1009 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:1013 | 29 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:1017 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/19d2:1171 | 28 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:1175 | 28 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:1179 | 28 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:1201 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:1216 | 13 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:1224 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:1227 | 8 + .../usbmodeswitch/data/usb_modeswitch.d/19d2:1514 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:1517 | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:1520 | 8 + .../usbmodeswitch/data/usb_modeswitch.d/19d2:1523 | 8 + .../usbmodeswitch/data/usb_modeswitch.d/19d2:1528 | 8 + .../data/usb_modeswitch.d/{19d2:1224 => 19d2:1542} | 10 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:2000 | 8 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:bccd | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:ffde | 7 + .../usbmodeswitch/data/usb_modeswitch.d/19d2:ffe6 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/19d2:fff5 | 8 +- .../usbmodeswitch/data/usb_modeswitch.d/19d2:fff6 | 6 - .../data/usb_modeswitch.d/{19d2:0003 => 1a8d:1000} | 12 +- .../data/usb_modeswitch.d/{19d2:0003 => 1a8d:2000} | 12 +- .../usbmodeswitch/data/usb_modeswitch.d/1ab7:5700 | 11 +- .../usbmodeswitch/data/usb_modeswitch.d/1b7d:0700 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/1bbb:000f | 6 + .../usbmodeswitch/data/usb_modeswitch.d/1bbb:00ca | 6 + .../usbmodeswitch/data/usb_modeswitch.d/1bbb:f000 | 11 +- .../usbmodeswitch/data/usb_modeswitch.d/1bbb:f017 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1bbb:f052 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1c9e:1001 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/1c9e:6061 | 7 - .../usbmodeswitch/data/usb_modeswitch.d/1c9e:9200 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/1c9e:9800 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/1c9e:98ff | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1c9e:9e00 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/1c9e:9e08 | 8 + .../usbmodeswitch/data/usb_modeswitch.d/1c9e:f000 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/1da5:f000 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1dd6:1000 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/1de1:1101 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1e0e:f000 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/1e89:f000 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1edf:6003 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/1ee8:0009 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/1ee8:0013 | 9 +- .../usbmodeswitch/data/usb_modeswitch.d/1ee8:0040 | 8 +- .../usbmodeswitch/data/usb_modeswitch.d/1ee8:004a | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1ee8:0054 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1ee8:0060 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1ee8:0063 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1ee8:0068 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1f28:0021 | 7 +- .../usbmodeswitch/data/usb_modeswitch.d/1fac:0032 | 7 - .../usbmodeswitch/data/usb_modeswitch.d/1fac:0130 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/1fac:0150 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/1fac:0151 | 5 + .../usbmodeswitch/data/usb_modeswitch.d/2001:a706 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/2001:a707 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/2001:a708 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/2001:a805 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/2001:a80b | 7 + .../usbmodeswitch/data/usb_modeswitch.d/201e:1023 | 9 + .../usbmodeswitch/data/usb_modeswitch.d/201e:2009 | 7 - .../usbmodeswitch/data/usb_modeswitch.d/2020:0002 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/2020:f00e | 8 +- .../usbmodeswitch/data/usb_modeswitch.d/2077:1000 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/2077:f000 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/21f5:1000 | 6 + .../data/usb_modeswitch.d/{19d2:0003 => 22de:6801} | 8 +- .../data/usb_modeswitch.d/{19d2:0003 => 22de:6803} | 8 +- .../usbmodeswitch/data/usb_modeswitch.d/22f4:0021 | 6 + .../usbmodeswitch/data/usb_modeswitch.d/230d:0001 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/230d:0007 | 6 - .../usbmodeswitch/data/usb_modeswitch.d/230d:0101 | 6 + .../usbmodeswitch/data/usb_modeswitch.d/2357:0200 | 7 + .../usbmodeswitch/data/usb_modeswitch.d/8888:6500 | 6 - .../src/router/usbmodeswitch/device_reference.txt | 2604 ---- release/src/router/usbmodeswitch/dispatcher.c | 88 + release/src/router/usbmodeswitch/jim/AUTHORS | 41 + release/src/router/usbmodeswitch/jim/LICENSE | 45 + release/src/router/usbmodeswitch/jim/Makefile.in | 198 + release/src/router/usbmodeswitch/jim/README | 235 + .../router/usbmodeswitch/jim/README.usb_modeswitch | 6 + release/src/router/usbmodeswitch/jim/auto.def | 436 + .../src/router/usbmodeswitch/jim/autosetup/LICENSE | 35 + .../usbmodeswitch/jim/autosetup/README.autosetup | 1 + .../router/usbmodeswitch/jim/autosetup/autosetup | 1820 +++ .../router/usbmodeswitch/jim/autosetup/cc-lib.tcl | 77 + .../usbmodeswitch/jim/autosetup/cc-shared.tcl | 63 + .../src/router/usbmodeswitch/jim/autosetup/cc.tcl | 660 + .../usbmodeswitch/jim/autosetup/config.guess | 1508 ++ .../router/usbmodeswitch/jim/autosetup/config.sub | 1739 +++ .../router/usbmodeswitch/jim/autosetup/find-tclsh | 17 + .../router/usbmodeswitch/jim/autosetup/system.tcl | 268 + .../router/usbmodeswitch/jim/autosetup/test-tclsh | 20 + release/src/router/usbmodeswitch/jim/bootstrap.tcl | 3 + release/src/router/usbmodeswitch/jim/configure | 3 + release/src/router/usbmodeswitch/jim/configure.ac | 1 + .../router/usbmodeswitch/jim/freebsd/andrew.txt | 65 + .../router/usbmodeswitch/jim/freebsd/clemens.txt | 87 + .../src/router/usbmodeswitch/jim/freebsd/duane.txt | 65 + .../router/usbmodeswitch/jim/freebsd/oharboe.txt | 85 + .../src/router/usbmodeswitch/jim/freebsd/pat.txt | 84 + .../router/usbmodeswitch/jim/freebsd/salvatore.txt | 88 + .../src/router/usbmodeswitch/jim/freebsd/uwe.txt | 73 + release/src/router/usbmodeswitch/jim/glob.tcl | 129 + release/src/router/usbmodeswitch/jim/initjimsh.tcl | 27 + release/src/router/usbmodeswitch/jim/jim-aio.c | 1353 ++ release/src/router/usbmodeswitch/jim/jim-array.c | 274 + release/src/router/usbmodeswitch/jim/jim-clock.c | 159 + .../src/router/usbmodeswitch/jim/jim-config.h.in | 2 + .../src/router/usbmodeswitch/jim/jim-eventloop.c | 760 + .../src/router/usbmodeswitch/jim/jim-eventloop.h | 87 + release/src/router/usbmodeswitch/jim/jim-exec.c | 1619 +++ release/src/router/usbmodeswitch/jim/jim-file.c | 929 ++ release/src/router/usbmodeswitch/jim/jim-format.c | 433 + .../src/router/usbmodeswitch/jim/jim-interactive.c | 137 + release/src/router/usbmodeswitch/jim/jim-load.c | 126 + release/src/router/usbmodeswitch/jim/jim-nvp.c | 338 + release/src/router/usbmodeswitch/jim/jim-nvp.h | 275 + release/src/router/usbmodeswitch/jim/jim-pack.c | 380 + release/src/router/usbmodeswitch/jim/jim-package.c | 259 + release/src/router/usbmodeswitch/jim/jim-posix.c | 231 + release/src/router/usbmodeswitch/jim/jim-readdir.c | 119 + .../src/router/usbmodeswitch/jim/jim-readline.c | 61 + release/src/router/usbmodeswitch/jim/jim-regexp.c | 566 + release/src/router/usbmodeswitch/jim/jim-sdl.c | 234 + release/src/router/usbmodeswitch/jim/jim-signal.c | 514 + release/src/router/usbmodeswitch/jim/jim-signal.h | 24 + release/src/router/usbmodeswitch/jim/jim-subcmd.c | 293 + release/src/router/usbmodeswitch/jim/jim-subcmd.h | 92 + release/src/router/usbmodeswitch/jim/jim-syslog.c | 190 + .../src/router/usbmodeswitch/jim/jim-win32compat.h | 69 + release/src/router/usbmodeswitch/jim/jim.c | 14478 +++++++++++++++++++ release/src/router/usbmodeswitch/jim/jim.h | 917 ++ .../src/router/usbmodeswitch/jim/jimautoconf.h.in | 145 + release/src/router/usbmodeswitch/jim/jimregexp.c | 1756 +++ release/src/router/usbmodeswitch/jim/jimregexp.h | 117 + release/src/router/usbmodeswitch/jim/jimsh.c | 111 + release/src/router/usbmodeswitch/jim/linenoise.c | 1379 ++ release/src/router/usbmodeswitch/jim/linenoise.h | 62 + .../router/usbmodeswitch/jim/make-bootstrap-jim | 104 + .../src/router/usbmodeswitch/jim/make-c-ext.tcl | 38 + release/src/router/usbmodeswitch/jim/make-index | 70 + .../usbmodeswitch/jim/make-load-static-exts.tcl | 48 + .../src/router/usbmodeswitch/jim/parse-unidata.tcl | 64 + release/src/router/usbmodeswitch/jim/regtest.tcl | 158 + release/src/router/usbmodeswitch/jim/rlprompt.tcl | 45 + release/src/router/usbmodeswitch/jim/stdlib.tcl | 150 + .../src/router/usbmodeswitch/jim/tcl.license.terms | 40 + release/src/router/usbmodeswitch/jim/tclcompat.tcl | 279 + release/src/router/usbmodeswitch/jim/tree.tcl | 219 + release/src/router/usbmodeswitch/jim/utf8.c | 192 + release/src/router/usbmodeswitch/jim/utf8.h | 110 + release/src/router/usbmodeswitch/make_string.tcl | 31 + release/src/router/usbmodeswitch/usb_modeswitch | Bin 0 -> 61213 bytes release/src/router/usbmodeswitch/usb_modeswitch.1 | 108 +- release/src/router/usbmodeswitch/usb_modeswitch.c | 843 +- .../src/router/usbmodeswitch/usb_modeswitch.conf | 14 +- release/src/router/usbmodeswitch/usb_modeswitch.h | 10 +- release/src/router/usbmodeswitch/usb_modeswitch.sh | 20 +- .../src/router/usbmodeswitch/usb_modeswitch.tcl | 905 +- .../router/usbmodeswitch/usb_modeswitch_dispatcher | 1135 ++ 399 files changed, 42498 insertions(+), 5509 deletions(-) create mode 100644 release/src/router/others/ntp2ip rewrite release/src/router/usbmodeswitch/data/40-usb_modeswitch.rules (75%) create mode 100755 release/src/router/usbmodeswitch/data/gen-rules.tcl create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0408:ea25 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0408:f000 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0618 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:061d create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:062c create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0632 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0637 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/04bb:bccd create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/04cc:225c create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:680c copy release/src/router/usbmodeswitch/data/usb_modeswitch.d/{04e8:f000 => 04e8:f000:sMo=U209} (54%) create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:0010 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=CELOT create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=DGT create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=StrongRising create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:6503 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0922:1001 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:4007 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7706 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8006 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8700 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:9200 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d001 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0e8d:0002 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/0e8d:0002:uPr=MT copy release/src/router/usbmodeswitch/data/usb_modeswitch.d/{19d2:0003 => 1004:614e} (52%) create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:6156 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61aa create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61dd create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61e7 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61eb create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:6327 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b11 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b14 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/109b:f009 rewrite release/src/router/usbmodeswitch/data/usb_modeswitch.d/1199:0fff (66%) create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14ba create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c3 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:151a create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1526 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:155b create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1805 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1c1b create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1c24 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1d50 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1f03 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1f11 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5023 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5059 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:7001 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/15eb:7153 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:6804 copy release/src/router/usbmodeswitch/data/usb_modeswitch.d/{19d2:0115 => 1726:1900} (61%) create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1726:f00e copy release/src/router/usbmodeswitch/data/usb_modeswitch.d/{19d2:0026 => 19d2:0031} (51%) copy release/src/router/usbmodeswitch/data/usb_modeswitch.d/{19d2:0083 => 19d2:0083:uPr=WCDMA} (58%) create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0120 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0146 copy release/src/router/usbmodeswitch/data/usb_modeswitch.d/{19d2:0083 => 19d2:0150} (50%) copy release/src/router/usbmodeswitch/data/usb_modeswitch.d/{19d2:1224 => 19d2:0154} (56%) create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0169 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0266 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0325 rewrite release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1001 (65%) rewrite release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1013 (65%) create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1017 rewrite release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1171 (65%) rewrite release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1175 (71%) rewrite release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1179 (65%) create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1227 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1520 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1523 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1528 copy release/src/router/usbmodeswitch/data/usb_modeswitch.d/{19d2:1224 => 19d2:1542} (56%) create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:ffde copy release/src/router/usbmodeswitch/data/usb_modeswitch.d/{19d2:0003 => 1a8d:1000} (52%) copy release/src/router/usbmodeswitch/data/usb_modeswitch.d/{19d2:0003 => 1a8d:2000} (52%) create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:000f create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:00ca create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:f017 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:f052 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:98ff create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9e08 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1da5:f000 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1de1:1101 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1e89:f000 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:004a create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0054 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0060 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0063 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0068 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0150 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0151 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a706 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a707 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a708 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a805 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a80b create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/201e:1023 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/2020:0002 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/2077:1000 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/2077:f000 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/21f5:1000 copy release/src/router/usbmodeswitch/data/usb_modeswitch.d/{19d2:0003 => 22de:6801} (58%) copy release/src/router/usbmodeswitch/data/usb_modeswitch.d/{19d2:0003 => 22de:6803} (58%) create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/22f4:0021 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/230d:0101 create mode 100644 release/src/router/usbmodeswitch/data/usb_modeswitch.d/2357:0200 delete mode 100755 release/src/router/usbmodeswitch/device_reference.txt create mode 100644 release/src/router/usbmodeswitch/dispatcher.c create mode 100644 release/src/router/usbmodeswitch/jim/AUTHORS create mode 100644 release/src/router/usbmodeswitch/jim/LICENSE create mode 100644 release/src/router/usbmodeswitch/jim/Makefile.in create mode 100644 release/src/router/usbmodeswitch/jim/README create mode 100644 release/src/router/usbmodeswitch/jim/README.usb_modeswitch create mode 100644 release/src/router/usbmodeswitch/jim/auto.def create mode 100644 release/src/router/usbmodeswitch/jim/autosetup/LICENSE create mode 100644 release/src/router/usbmodeswitch/jim/autosetup/README.autosetup create mode 100755 release/src/router/usbmodeswitch/jim/autosetup/autosetup create mode 100644 release/src/router/usbmodeswitch/jim/autosetup/cc-lib.tcl create mode 100644 release/src/router/usbmodeswitch/jim/autosetup/cc-shared.tcl create mode 100644 release/src/router/usbmodeswitch/jim/autosetup/cc.tcl create mode 100755 release/src/router/usbmodeswitch/jim/autosetup/config.guess create mode 100755 release/src/router/usbmodeswitch/jim/autosetup/config.sub create mode 100755 release/src/router/usbmodeswitch/jim/autosetup/find-tclsh create mode 100644 release/src/router/usbmodeswitch/jim/autosetup/system.tcl create mode 100644 release/src/router/usbmodeswitch/jim/autosetup/test-tclsh create mode 100644 release/src/router/usbmodeswitch/jim/bootstrap.tcl create mode 100755 release/src/router/usbmodeswitch/jim/configure create mode 100644 release/src/router/usbmodeswitch/jim/configure.ac create mode 100644 release/src/router/usbmodeswitch/jim/freebsd/andrew.txt create mode 100644 release/src/router/usbmodeswitch/jim/freebsd/clemens.txt create mode 100644 release/src/router/usbmodeswitch/jim/freebsd/duane.txt create mode 100644 release/src/router/usbmodeswitch/jim/freebsd/oharboe.txt create mode 100644 release/src/router/usbmodeswitch/jim/freebsd/pat.txt create mode 100644 release/src/router/usbmodeswitch/jim/freebsd/salvatore.txt create mode 100644 release/src/router/usbmodeswitch/jim/freebsd/uwe.txt create mode 100644 release/src/router/usbmodeswitch/jim/glob.tcl create mode 100644 release/src/router/usbmodeswitch/jim/initjimsh.tcl create mode 100644 release/src/router/usbmodeswitch/jim/jim-aio.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-array.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-clock.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-config.h.in create mode 100644 release/src/router/usbmodeswitch/jim/jim-eventloop.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-eventloop.h create mode 100644 release/src/router/usbmodeswitch/jim/jim-exec.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-file.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-format.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-interactive.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-load.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-nvp.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-nvp.h create mode 100644 release/src/router/usbmodeswitch/jim/jim-pack.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-package.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-posix.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-readdir.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-readline.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-regexp.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-sdl.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-signal.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-signal.h create mode 100644 release/src/router/usbmodeswitch/jim/jim-subcmd.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-subcmd.h create mode 100644 release/src/router/usbmodeswitch/jim/jim-syslog.c create mode 100644 release/src/router/usbmodeswitch/jim/jim-win32compat.h create mode 100644 release/src/router/usbmodeswitch/jim/jim.c create mode 100644 release/src/router/usbmodeswitch/jim/jim.h create mode 100644 release/src/router/usbmodeswitch/jim/jimautoconf.h.in create mode 100644 release/src/router/usbmodeswitch/jim/jimregexp.c create mode 100644 release/src/router/usbmodeswitch/jim/jimregexp.h create mode 100644 release/src/router/usbmodeswitch/jim/jimsh.c create mode 100644 release/src/router/usbmodeswitch/jim/linenoise.c create mode 100644 release/src/router/usbmodeswitch/jim/linenoise.h create mode 100755 release/src/router/usbmodeswitch/jim/make-bootstrap-jim create mode 100644 release/src/router/usbmodeswitch/jim/make-c-ext.tcl create mode 100755 release/src/router/usbmodeswitch/jim/make-index create mode 100644 release/src/router/usbmodeswitch/jim/make-load-static-exts.tcl create mode 100644 release/src/router/usbmodeswitch/jim/parse-unidata.tcl create mode 100644 release/src/router/usbmodeswitch/jim/regtest.tcl create mode 100644 release/src/router/usbmodeswitch/jim/rlprompt.tcl create mode 100644 release/src/router/usbmodeswitch/jim/stdlib.tcl create mode 100644 release/src/router/usbmodeswitch/jim/tcl.license.terms create mode 100644 release/src/router/usbmodeswitch/jim/tclcompat.tcl create mode 100644 release/src/router/usbmodeswitch/jim/tree.tcl create mode 100644 release/src/router/usbmodeswitch/jim/utf8.c create mode 100644 release/src/router/usbmodeswitch/jim/utf8.h create mode 100755 release/src/router/usbmodeswitch/make_string.tcl create mode 100755 release/src/router/usbmodeswitch/usb_modeswitch create mode 100644 release/src/router/usbmodeswitch/usb_modeswitch_dispatcher diff --git a/release/src/router/others/Makefile b/release/src/router/others/Makefile index af7e484690..84d15ecf41 100644 --- a/release/src/router/others/Makefile +++ b/release/src/router/others/Makefile @@ -18,6 +18,10 @@ ifeq ($(CONFIG_LINUX26),y) endif endif +ifeq ($(TCONFIG_DNSCRYPT),y) + install -m 0700 ntp2ip $(INSTALLDIR)/usr/sbin +endif + # Ethernet state report install -m 0700 ethstate $(INSTALLDIR)/usr/sbin diff --git a/release/src/router/others/ntp2ip b/release/src/router/others/ntp2ip new file mode 100644 index 0000000000..5c330b6c61 --- /dev/null +++ b/release/src/router/others/ntp2ip @@ -0,0 +1,13 @@ +#!/bin/sh + +#cleaning +sed -i /tmp/etc/hosts -e "/ntp2ip/d" + +for server in $(nvram get ntp_server) +do + addresses=$(hostip $server) + for address in $addresses + do + echo "$address $server #ntp2ip" >> /tmp/etc/hosts + done +done diff --git a/release/src/router/others/switch3g b/release/src/router/others/switch3g index cb2b4ca065..a9f6c51cde 100755 --- a/release/src/router/others/switch3g +++ b/release/src/router/others/switch3g @@ -62,9 +62,11 @@ else # vendor:product - if [ "$SEARCH" == "1" ]; then - logger 3G MODEM FOUND - $SWITCH - Switching ... - /usr/sbin/usb_modeswitch -Q -c /etc/usb_modeswitch.d/$SWITCH + if [ "$SEARCH" == "1" ]; then + logger 3G MODEM FOUND - $SWITCH - Switching ... + DV=`echo $SWITCH | cut -d ":" -f1` + DP=`echo $SWITCH | cut -d ":" -f2` + /usr/sbin/usb_modeswitch -Q -c /etc/usb_modeswitch.d/$SWITCH -v $DV -p $DP TEST1=`cat /etc/usb_modeswitch.d/$SWITCH | grep "TargetVendor" | cut -d "=" -f2 | wc -l` if [ "$TEST1" == "1" ]; then diff --git a/release/src/router/usbmodeswitch/ChangeLog b/release/src/router/usbmodeswitch/ChangeLog index 6d1603b369..bed25873d0 100644 --- a/release/src/router/usbmodeswitch/ChangeLog +++ b/release/src/router/usbmodeswitch/ChangeLog @@ -2,6 +2,53 @@ History of USB_ModeSwitch ========================= +Version 1.2.6, 2013/06/02 + Several changes to streamline compiling as part of larger projects + (thanks to Nicolas Carrier), mostly in Makefile; fix compiler warnings + appearing in certain build environments (N. Carrier); new Quanta + procedure (thanks to Andrey Tikhomirov) for Quanta 1K3 LTE; fix for + error with cascaded hubs in dispatcher script (hint from N. Carrier) +Version 1.2.5, 2012/11/09 + Initial support for MBIM devices, use with data package >= 20121109; + checking for these is the automatic default, new parameter NoMBIMCheck + prevents the check per device in case of problems; new global option + to set "delay_use" of usb-storage (as low values may prevent + mode-switching); fix for handling multi-configuration devices (thanks + to Bjørn Mork for advice) +Version 1.2.4, 2012/08/12 + Additional interface checks to prevent sending UFI commands to non- + storage interfaces (prompted by more ambiguous device IDs popping up); + change in SierraMode for handling newer devices which caused an error + abort before; Makefile fix for parallelized make runs +Version 1.2.3, 2012/01/28 + Fixed two bugs both causing the embedded-jimsh install variant of the + dispatcher crash (the "pure-script" install variant was NOT affected); + fixed some "regexp" incompatibilities with Debian's libjim +Version 1.2.2, 2012/01/19 + Fixed bad bug preventing mode switch for devices using TargetClass; + improved logging in case of negative success check +Version 1.2.1, 2011/12/26 + Fixed possible ambiguities when multiple identical modems are plugged + synchronously; this is achieved by adding -b and -g parameter (busnum + and devnum) for proper id'ing; some resulting workflow changes and + shortcuts in "system integration" mode (use sysfs/wrapper for success + control, reduce informative checks); improved hub usage robustness; + future data packages can be ridded of redundancies (default ID, success + check parameters), resulting in smaller files; + fixed bad bug which may prevent switching completely when logging is + not activated; fixed possible overflow in dispatcher C code (thanks to + Gilles Espinasse) +Version 1.2.0, 2011/10/23 + Added QisdaMode for Qisda H21 (thanks to Chi-Hang Long for the code); + dispatcher can now be installed with an embedded interpreter, so that + Tcl is no longer required; added command line options for binary program + to accept configuration data via stdin or as a long string parameter - + this fixes the bug with non-writable temporary file during boot; + reversed skipping of pre-switching delay, which has caused problems; + fixed potential buffer overflow (thanks to Rafael Silva for the find); + get first interface right even on some broken devices (thanks to + Alexander Gordeev for the patch); increased post-switch delay before + driver binding to avoid possible conflict with usb-storage Version 1.1.9, 2011/08/05 Added CiscoMode for Valet device; additional checking for CDC ACM device to prevent erroneous driver loading after switching; no more post-switch diff --git a/release/src/router/usbmodeswitch/Makefile b/release/src/router/usbmodeswitch/Makefile index 09c3afbe34..6d4204a57e 100644 --- a/release/src/router/usbmodeswitch/Makefile +++ b/release/src/router/usbmodeswitch/Makefile @@ -1,5 +1,5 @@ PROG = usb_modeswitch -VERS = 1.1.9 +VERS = 1.2.6 CC = gcc CFLAGS += -Wall LIBS = -lusb-1.0 @@ -10,27 +10,86 @@ ETCDIR = $(DESTDIR)/etc UDEVDIR = $(DESTDIR)/lib/udev SBINDIR = $(PREFIX)/sbin MANDIR = $(PREFIX)/share/man/man1 +VPATH = jimtcl +HOST_TCL := $(shell cd jim && ./autosetup/find-tclsh) +ifeq (,$(findstring jimsh0,$(HOST_TCL))) +TCL ?= $(HOST_TCL) +else +TCL ?= /usr/bin/tclsh +endif +JIM_CONFIGURE_OPTS =--disable-lineedit \ + --with-out-jim-ext="stdlib posix load signal syslog" --prefix=/usr -.PHONY: clean install uninstall +.PHONY: clean install install-common uninstall \ + script shared static \ + dispatcher-script dispatcher-shared dispatcher-static \ + install-script install-shared install-static -all: $(PROG) +all: script -$(PROG): $(OBJS) +script: $(PROG) dispatcher-script + +shared: $(PROG) dispatcher-shared + +static: $(PROG) dispatcher-static + +$(PROG): $(OBJS) usb_modeswitch.h $(CC) -o $(PROG) $(OBJS) $(CFLAGS) $(LIBS) $(LDFLAGS) +jim/libjim.so: + cd jim && CFLAGS="$(CFLAGS)" CC="$(CC)" ./configure $(JIM_CONFIGURE_OPTS) --shared + $(MAKE) -C jim lib + +jim/libjim.a: + cd jim && CFLAGS="$(CFLAGS)" CC="$(CC)" ./configure $(JIM_CONFIGURE_OPTS) + $(MAKE) -C jim lib + +dispatcher-script: usb_modeswitch.tcl + sed 's_!/usr/bin/tclsh_!'"$(TCL)"'_' < usb_modeswitch.tcl > usb_modeswitch_dispatcher + +dispatcher-shared: jim/libjim.so dispatcher.c usb_modeswitch.string + $(CC) dispatcher.c $(LDFLAGS) -Ljim -ljim -Ijim -o usb_modeswitch_dispatcher $(CFLAGS) + +dispatcher-static: jim/libjim.a dispatcher.c usb_modeswitch.string + $(CC) dispatcher.c $(LDFLAGS) jim/libjim.a -Ijim -o usb_modeswitch_dispatcher $(CFLAGS) + +usb_modeswitch.string: usb_modeswitch.tcl + $(HOST_TCL) make_string.tcl usb_modeswitch.tcl > $@ + clean: $(RM) usb_modeswitch + $(RM) usb_modeswitch_dispatcher + $(RM) usb_modeswitch.string + $(RM) jim/autosetup/jimsh0 + $(RM) jim/autosetup/jimsh0.c + +distclean: clean + -$(MAKE) -C jim distclean + +ums-clean: + $(RM) usb_modeswitch + $(RM) usb_modeswitch_dispatcher + $(RM) usb_modeswitch.string -install: all - install -D -s --mode=755 usb_modeswitch $(SBINDIR)/usb_modeswitch - install -D --mode=755 usb_modeswitch.tcl $(SBINDIR)/usb_modeswitch_dispatcher +install-common: $(PROG) usb_modeswitch_dispatcher + install -D --mode=755 usb_modeswitch $(SBINDIR)/usb_modeswitch install -D --mode=755 usb_modeswitch.sh $(UDEVDIR)/usb_modeswitch install -D --mode=644 usb_modeswitch.conf $(ETCDIR)/usb_modeswitch.conf install -D --mode=644 usb_modeswitch.1 $(MANDIR)/usb_modeswitch.1 + install -D --mode=755 usb_modeswitch_dispatcher $(SBINDIR)/usb_modeswitch_dispatcher install -d $(DESTDIR)/var/lib/usb_modeswitch +install: install-script + +install-script: dispatcher-script install-common + +install-shared: dispatcher-shared install-common + +install-static: dispatcher-static install-common + uninstall: $(RM) $(SBINDIR)/usb_modeswitch + $(RM) $(SBINDIR)/usb_modeswitch_dispatcher $(RM) $(UDEVDIR)/usb_modeswitch $(RM) $(ETCDIR)/usb_modeswitch.conf $(RM) $(MANDIR)/usb_modeswitch.1 diff --git a/release/src/router/usbmodeswitch/README b/release/src/router/usbmodeswitch/README index 8ff17ce167..5a07c3e78d 100644 --- a/release/src/router/usbmodeswitch/README +++ b/release/src/router/usbmodeswitch/README @@ -10,17 +10,21 @@ What it is USB_ModeSwitch is (surprise!) a small mode switching tool for controlling "flip flop" (multiple mode) USB devices. -Several new USB devices (especially high-speed WAN stuff, based on cell phone -chipsets containing that feature) have their MS Windows drivers onboard; when -plugged in for the first time they act like a flash storage and start -installing the driver from there. +More and more USB devices have their MS Windows drivers onboard; when plugged +in for the first time they act like a flash storage and offer their driver +installation from there. After installation (and on every consecutive plugging) the driver switches the mode internally, the storage device vanishes (in most cases), and a new device -(like an USB modem) shows up. Modem maker "Option" calls that feature "ZeroCD -(TM)" since it eliminates the need for shipping a separate driver carrier. +(like an USB modem) shows up. + +At first this feature appeared on devices with cell phone chipsets, presumably +because some of them were able to change the mode of their USB port anyway +from storage to communication - so why not make use of this in a modem stick? +Modem maker "Option" calls that feature "ZeroCD (TM)" since it eliminates the +need for shipping a separate driver carrier. In the beginning, nothing of this was documented in any form and there was -hardly any Linux support available. +hardly any Linux/Unix support available. On the good side, most of the known devices are working out of the box in all modes with the available Linux modules like "usb-storage" or serial USB drivers. That leaves only the problem of the mode-switching from storage to whatever @@ -29,14 +33,18 @@ the thing is supposed to do. Fortunately there are things like human intelligence, USB sniffing programs and "libusb". It is possible to eavesdrop the communication of the MS Windows driver, to isolate the command or action that does the switching, and to replay -the same sequence in Linux. +the same sequence in the Unix system. USB_ModeSwitch makes this process easy to handle by taking the relevant para- meters from a configuration file and doing all the initialization and communi- cation stuff, with heavy help from "libusb". -It is mainly used automatically - via udev events and rules - to do the switch -without any user interaction. But it can also be run as a command line tool, -usually when trying to make unknown devices work with it. + +In Linux and friends it is intended to be used automatically - via udev events +and rules - and doing the mode switch without any user interaction. +However, the core program should be as portable als libusb itself; it does not +rely on specific Linux features. +It can be run as an interactive command line tool, particularly useful when trying +to manage hitherto unknown devices. We have already collected a wide range of information on how to switch all sorts of devices. If you run into a new one that is unknown yet, don't despair: @@ -49,57 +57,86 @@ How to install !! You need the usb-modeswitch-data package from the same source as this !! If you have an earlier version installed, de-installation is recommended ("make -uninstall"). The wrapper script location changed in 1.1.0, old ones might be -orphaned in /usr/sbin. +uninstall") but not mandatory. The wrapper script location changed in 1.1.0; +old files might be orphaned but will not do any harm. + +The main prerequisite for installing from source is package "libusb-dev" !! + +To install the tool set, unpack and run the install command (see below) in the +newly created directory. + +From version 1.2.0, there are three flavours of installing. The only difference +between those is the way the dispatcher is installed, but this affects the +dependencies as well: + +1. If you have the "Tcl" scripting language available on your system (packages + "tcl" or "jimsh"), use the light-weight installation: + + # make install + +2. If you are size-constrained and have the Jimsh library on your system + (package "libjim-dev"), you can have the Tcl interpreter embedded with the + dispatcher, using its shared lib: + + # make install-shared -Important: you need "tcl" for the wrapper script to work; if you enter "tclsh" -and you get a "%" prompt, you are set (to exit type "exit"). The "tcl" package -is part of all distributions I know. Use version 8.4 and higher. -If you are space-restricted you can also use the "jimsh" mini Tcl interpreter. The -Tcl wrapper is compatible with the limited command set of this small shell. Since -version 1.1.7 an alternative source package containing jimsh will be provided. -The typical size of jimsh is 140 kB, or 70 kB in a compressed file system. +3. If you are size-constrained and definitely don't need a Tcl interpreter + for anything else, choose the statically embedded flavour. This will have + no further dependency as it uses the included interpreter code, which is + configured for small size: -To install the tool set, unpack and in the newly created directory use this line -at the shell (as root): + # make install-static -> make install +Note that the "static"/"shared" targets are NOT referring to the usb_modeswitch +program, only to the dispatcher! +Any one of these commands will install a small posix shell script, the +dispatcher (wrapper) as script or as binary, a global config file, the core +program and a man page. -This installs the small shell script, the dispatcher (wrapper) script, a global -config file, the binary and a man page. If you chose the package with jimsh, the -Tcl shell is configured, built and installed in the same step, optimized for -size. +Install the data package as well and you are set. -Install the data package as well and you are set already; if your device is -known, you should be able to just plug it and use it. If it doesn't work - -we will find out why not. +NOTE: installing over (possibly outdated) Linux distribution packages of this +program and the data collection should not be a problem. + + +How to use +========== + +If your device is known, you should be able to just plug it and use it. If +it doesn't work - we will find out why. For manual use just run "usb_modeswitch" (as root). Work with the command line interface or with a setup file. You can use any file and give its path with the "-c" parameter. -The file named "device_reference.txt" that you can find in this package is -a device and configuration reference containing most known devices; you can -use it as a base to create your own configuration file. +The file named "device_reference.txt" that you can find on the home site of +this package is a device and configuration reference containing most known +devices; you can use it as a "database" to create your own configuration file. It's heavily commented and should tell you what to do. It also contains a thorough explanation of all the parameters. Run "usb_modeswitch -h" to list the command line parameters. See also the provided man page. -Note: manual use is intended for testing and analyzing ! +Important note: Manual use is mainly intended for testing and analyzing!! +The program puts no limits on what you can send to your USB device, so I +assume it is possible to screw it up profoundly. + +Once your new device is switching fine you can add it to the data files to +make the process automatic. -Once your new device is switching fine you can add a rule entry to the rules -file to let udev run usb_modeswitch as soon as the default IDs are found -(when the device is plugged). If you look into the rules file you will see -immediately how your new entry should look like. +For this to work, add a rule entry to the rules file to let udev run +usb_modeswitch as soon as the default IDs are found (when the device is +plugged). If you look into the rules file you will see immediately how +your new entry should look like. The location is: /lib/udev/rules.d/40-usb_modeswitch.rules -Then add your new setup file to the folder -"/usr/share/usb_modeswitch". And don't forget to report your success !! +Then add your new config file to the folder +"/etc/usb_modeswitch.d" (only for custom config files!). +And don't forget to report your success !! -Again, mind that the rules file and the config folder are installed by -the usb_modeswitch data package. +Again, remember that the rules file and the default device config folder +(/usr/share/usb_modeswitch) are installed by the usb_modeswitch data package. ########## @@ -124,7 +161,7 @@ not supported yet. You could try this approach: Note the device's vendor and product ID from /proc/bus/usb/devices (or from the output of lsusb); the assigned driver is usually "usb-storage". Then try spying -on the USB communication to the device with the same ID inside M$ Windoze. I +on the USB communication to the device with the same ID inside MS Windoze. I recommend this tool: "SniffUSB" (http://benoit.papillault.free.fr/usbsnoop/index.php.en). @@ -135,13 +172,16 @@ forum (see above) or send it to the author (see below)! Whodunit ======== -Copyright 2007, 2008, 2009, 2010 Josua Dietze (mail to "usb_admin" +Copyright 2007, 2008, 2009, 2010, 2011, 2012 Josua Dietze (mail to "usb_admin" at the domain "draisberghof.de" or write a personal message through the forum - to "Josh"; NO SUPPORT QUESTIONS VIA E-MAIL, use the forum!) + to "Josh") -Command line parsing, decent usage/config output and handling, advanced options - and bugfixes: - Joakim Wennergren (jokedst) (gmail.com) + !!! NO SUPPORT QUESTIONS VIA E-MAIL, use the forum !!! + +Major contributions: + +Command line parsing and other essential contributions: + Joakim Wennergren TargetClass parameter implementation to support new Option devices/firmware: Paul Hardwick (http://www.pharscape.org) @@ -150,7 +190,7 @@ Created with initial help from: "usbsnoop2libusb.pl" by Timo Lindfors (http://iki.fi/lindi/usb/usbsnoop2libusb.pl) -Config file parsing stuff borrowed from: +Config file parsing code borrowed from: Guillaume Dargaud (http://www.gdargaud.net/Hack/SourceCode.html) Hexstr2bin function borrowed from: @@ -173,9 +213,17 @@ Code, fixes and ideas from: Nils Radtke Filip Aben Amit Mendapara + Roman S. Samarev + Chi-Hang Long + Andrey Tikhomirov Device information contributors are named in the "device_reference.txt" file. +JimTcl is currently maintained by Steve Bennett; see README in subfolder + for details. It is released under a FreeBSD-style license. + Visit http://jim.berlios.de/ + + Legal ===== @@ -196,4 +244,4 @@ Or find it as the file COPYING in this folder. -Last revised: 2011-02-25, Josua Dietze +Last revised: 2013-06-02, Josua Dietze diff --git a/release/src/router/usbmodeswitch/data/40-usb_modeswitch.rules b/release/src/router/usbmodeswitch/data/40-usb_modeswitch.rules dissimilarity index 75% index ad56aea692..aebdb6cc0c 100644 --- a/release/src/router/usbmodeswitch/data/40-usb_modeswitch.rules +++ b/release/src/router/usbmodeswitch/data/40-usb_modeswitch.rules @@ -1,598 +1,879 @@ -# Part of usb-modeswitch-data, version 20110805 -# -# This file is intended for USB_ModeSwitch version >= 1.1.2 -# but will not break anything if used with versions >= 1.0.3 -# -ACTION!="add", GOTO="modeswitch_rules_end" - -# This adds a symlink "gsmmodem[n]" to the lowest ttyUSB port with interrupt -# transfer; checked against a list of known modems, or else no action -KERNEL=="ttyUSB*", ATTRS{bNumConfigurations}=="*", PROGRAM="usb_modeswitch --symlink-name %p %s{idVendor} %s{idProduct} %E{PRODUCT}", SYMLINK="%c" - -SUBSYSTEM!="usb", GOTO="modeswitch_rules_end" - -# This adds the device ID to the "option" driver after a warm boot -# in cases when the device is yet unknown to the driver; checked -# against a list of known modems, or else no action -ATTR{bInterfaceClass}=="ff", ATTR{bInterfaceNumber}=="00", ATTRS{bNumConfigurations}=="*", RUN+="usb_modeswitch --driver-bind %p %s{idVendor} %s{idProduct} %E{PRODUCT}" - -# Most known install partitions are on interface 0, one on 5, one on 9 -ATTRS{bInterfaceNumber}!="0[059]", GOTO="modeswitch_rules_end" - -# only storage class devices are handled; negative -# filtering here would exclude some quirky devices -ATTRS{bDeviceClass}=="08", GOTO="modeswitch_rules_begin" -ATTRS{bInterfaceClass}=="0[38]", GOTO="modeswitch_rules_begin" -GOTO="modeswitch_rules_end" - - -LABEL="modeswitch_rules_begin" - -# HP LaserJet Professional P1102 -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="002a", RUN+="usb_modeswitch '%b/%k'" - -# Nokia CS-10 -ATTRS{idVendor}=="0421", ATTRS{idProduct}=="060c", RUN+="usb_modeswitch '%b/%k'" - -# Nokia CS-15 -ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0610", RUN+="usb_modeswitch '%b/%k'" - -# Nokia CS-17 -ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0622", RUN+="usb_modeswitch '%b/%k'" - -# Nokia CS-18 -ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0627", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone MD950 (Wisue Technology), Philips (?) -ATTRS{idVendor}=="0471", ATTRS{idProduct}=="1210", RUN+="usb_modeswitch '%b/%k'" - -# Philips TalkTalk (NXP Semiconductors "Dragonfly") -ATTRS{idVendor}=="0471", ATTRS{idProduct}=="1237", RUN+="usb_modeswitch '%b/%k'" - -# Kyocera W06K CDMA modem -ATTRS{idVendor}=="0482", ATTRS{idProduct}=="024d", RUN+="usb_modeswitch '%b/%k'" - -# Samsung GT-B3730 -ATTRS{idVendor}=="04e8", ATTRS{idProduct}=="689a", RUN+="usb_modeswitch '%b/%k'" - -# Samsung U209 -ATTRS{idVendor}=="04e8", ATTRS{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" - -# AVM Fritz!Wlan USB Stick N -ATTRS{idVendor}=="057c", ATTRS{idProduct}=="84ff", RUN+="usb_modeswitch '%b/%k'" - -# Prolink P2000 CDMA, Samsung SGH-Z810, Older Option devices, Vertex Wireless 100 Series, AnyDATA devices, Bless UC165 -ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" - -# HSDPA USB modem from dealextreme -ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="2000", RUN+="usb_modeswitch '%b/%k'" - -# D-Link DWM-162-U5, Micromax MMX 300c -ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="2001", RUN+="usb_modeswitch '%b/%k'" - -# Siptune LM-75 ("LinuxModem") -ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" - -# Qtronix EVDO 3G Modem (for TianYi) -ATTRS{idVendor}=="05c7", ATTRS{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" - -# InfoCert Business Key (SmartCard/Reader emulation) -ATTRS{idVendor}=="072f", ATTRS{idProduct}=="100d", RUN+="usb_modeswitch '%b/%k'" - -# D-Link DWM-156 HSUPA 3.75G USB Modem -ATTRS{idVendor}=="07d1", ATTRS{idProduct}=="a800", RUN+="usb_modeswitch '%b/%k'" - -# D-Link DWM-156 HSUPA 3.75G USB Modem -ATTRS{idVendor}=="07d1", ATTRS{idProduct}=="a804", RUN+="usb_modeswitch '%b/%k'" - -# Toshiba G450 -ATTRS{idVendor}=="0930", ATTRS{idProduct}=="0d46", RUN+="usb_modeswitch '%b/%k'" - -# Zydas ZD1211RW WLAN USB, Sphairon HomeLink 1202 (Variant 1) -ATTRS{idVendor}=="0ace", ATTRS{idProduct}=="2011", RUN+="usb_modeswitch '%b/%k'" - -# Zydas ZD1211RW WLAN USB, Sphairon HomeLink 1202 (Variant 2) -ATTRS{idVendor}=="0ace", ATTRS{idProduct}=="20ff", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="6711", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="6731", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="6751", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="6771", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="6791", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="6811", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="6911", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="6951", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="6971", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7011", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7031", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7051", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7071", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7111", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7211", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7251", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7271", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7301", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7311", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7361", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7381", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7401", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7501", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7601", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7701", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7801", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7901", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7a01", RUN+="usb_modeswitch '%b/%k'" - -# Option iCon 461 -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="7a05", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="8200", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="8201", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="8300", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="8302", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="8304", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="8400", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="8600", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="8800", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="8900", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="9000", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="c031", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="c100", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="d013", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="d031", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="d033", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="d035", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="d055", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="d057", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="d058", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="d155", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="d157", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="d255", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="d257", RUN+="usb_modeswitch '%b/%k'" - -# Option HSO device -ATTRS{idVendor}=="0af0", ATTRS{idProduct}=="d357", RUN+="usb_modeswitch '%b/%k'" - -# Olivetti Olicard 100 and others -ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c700", RUN+="usb_modeswitch '%b/%k'" - -# Olivetti Olicard 145, 155 -ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" - -# Atheros Wireless / Netgear WNDA3200 -ATTRS{idVendor}=="0cf3", ATTRS{idProduct}=="20ff", RUN+="usb_modeswitch '%b/%k'" - -# Kobil mIdentity 3G (1) -ATTRS{idVendor}=="0d46", ATTRS{idProduct}=="45a1", RUN+="usb_modeswitch '%b/%k'" - -# Kobil mIdentity 3G (2) -ATTRS{idVendor}=="0d46", ATTRS{idProduct}=="45a5", RUN+="usb_modeswitch '%b/%k'" - -# Mobile Action ("Smart Cable") -ATTRS{idVendor}=="0df7", ATTRS{idProduct}=="0800", RUN+="usb_modeswitch '%b/%k'" - -# MediaTek Wimax USB Card -ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="7109", RUN+="usb_modeswitch '%b/%k'" - -# Sony Ericsson MD300 -ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0cf", RUN+="usb_modeswitch '%b/%k'" - -# Sony Ericsson MD400 -ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0e1", RUN+="usb_modeswitch '%b/%k'" - -# Sony Ericsson MD400G -ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d103", RUN+="usb_modeswitch '%b/%k'" - -# GW D301 (Advinne AMC) -ATTRS{idVendor}=="0fd1", ATTRS{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" - -# LG LDU-1900D EV-DO (Rev. A) -ATTRS{idVendor}=="1004", ATTRS{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" - -# LG HDM-2100 (EVDO Rev.A USB modem) -ATTRS{idVendor}=="1004", ATTRS{idProduct}=="607f", RUN+="usb_modeswitch '%b/%k'" - -# LG L-05A -ATTRS{idVendor}=="1004", ATTRS{idProduct}=="613a", RUN+="usb_modeswitch '%b/%k'" - -# LG LUU-2100TI (aka AT&T USBConnect Turbo) -ATTRS{idVendor}=="1004", ATTRS{idProduct}=="613f", RUN+="usb_modeswitch '%b/%k'" - -# LG AD600 -ATTRS{idVendor}=="1004", ATTRS{idProduct}=="6190", RUN+="usb_modeswitch '%b/%k'" - -# Huawei E630 -ATTRS{idVendor}=="1033", ATTRS{idProduct}=="0035", RUN+="usb_modeswitch '%b/%k'" - -# UTStarcom UM175 (distributor "Alltel") -ATTRS{idVendor}=="106c", ATTRS{idProduct}=="3b03", RUN+="usb_modeswitch '%b/%k'" - -# Pantech / UTStarcom UMW190 (Verizon) -ATTRS{idVendor}=="106c", ATTRS{idProduct}=="3b05", RUN+="usb_modeswitch '%b/%k'" - -# UTStarcom UM185E (distributor "Alltel") -ATTRS{idVendor}=="106c", ATTRS{idProduct}=="3b06", RUN+="usb_modeswitch '%b/%k'" - -# Sagem F@ST 9520-35-GLR -ATTRS{idVendor}=="1076", ATTRS{idProduct}=="7f40", RUN+="usb_modeswitch '%b/%k'" - -# Sierra devices (specific driver) -ATTRS{idVendor}=="1199", ATTRS{idProduct}=="0fff", RUN+="usb_modeswitch '%b/%k'" - -# Digicom 8E4455 (and all Pirelli devices - EXPERIMENTAL) -ATTRS{idVendor}=="1266", ATTRS{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" - -# Huawei E169 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1001", RUN+="usb_modeswitch '%b/%k'" - -# Huawei E220, E230, E270, E870 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1003", RUN+="usb_modeswitch '%b/%k'" - -# Huawei V725 Phone (aka Vodafone 725) -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1009", RUN+="usb_modeswitch '%b/%k'" - -# Huawei U7510 / U7517 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="101e", RUN+="usb_modeswitch '%b/%k'" - -# Huawei U8220, T-Mobile Pulse (Android smartphone) -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1030", RUN+="usb_modeswitch '%b/%k'" - -# Huawei U8110 / Joy, Vodafone 845 (Android smartphone) -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1031", RUN+="usb_modeswitch '%b/%k'" - -# Huawei E180 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1414", RUN+="usb_modeswitch '%b/%k'" - -# Huawei, newer modems -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1446", RUN+="usb_modeswitch '%b/%k'" - -# Huawei E352 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1449", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone (Huawei) K3806 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="14ad", RUN+="usb_modeswitch '%b/%k'" - -# Huawei E173 (Viettel 3G) -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="14b5", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone (Huawei) K4511 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="14b7", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone (Huawei) K4605 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="14c1", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone/Huawei K3771 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="14c4", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone (Huawei) K4510 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="14c5", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone/Huawei K3770 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="14d1", RUN+="usb_modeswitch '%b/%k'" - -# T-Mobile NL (Huawei E352) -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="14fe", RUN+="usb_modeswitch '%b/%k'" - -# Huawei EC156 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1505", RUN+="usb_modeswitch '%b/%k'" - -# Huawei K3765 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1520", RUN+="usb_modeswitch '%b/%k'" - -# Huawei K4505 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1521", RUN+="usb_modeswitch '%b/%k'" - -# Huawei R201 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1523", RUN+="usb_modeswitch '%b/%k'" - -# Huawei E1553 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1553", RUN+="usb_modeswitch '%b/%k'" - -# Huawei E173 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1557", RUN+="usb_modeswitch '%b/%k'" - -# Huawei E173s -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1c0b", RUN+="usb_modeswitch '%b/%k'" - -# Huawei ET8282 -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1da1", RUN+="usb_modeswitch '%b/%k'" - -# Huawei BM358 WiMAX -ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="380b", RUN+="usb_modeswitch '%b/%k'" - -# Cisco AM10 "Valet Connector" -ATTRS{idVendor}=="1307", ATTRS{idProduct}=="1169", RUN+="usb_modeswitch '%b/%k'" - -# Novatel Wireless devices -ATTRS{idVendor}=="1410", ATTRS{idProduct}=="5010", RUN+="usb_modeswitch '%b/%k'" - -# Novatel MC990D -ATTRS{idVendor}=="1410", ATTRS{idProduct}=="5020", RUN+="usb_modeswitch '%b/%k'" - -# Novatel U760 -ATTRS{idVendor}=="1410", ATTRS{idProduct}=="5030", RUN+="usb_modeswitch '%b/%k'" - -# Novatel MC760 3G -ATTRS{idVendor}=="1410", ATTRS{idProduct}=="5031", RUN+="usb_modeswitch '%b/%k'" - -# Novatel Generic MiFi 2352 / Vodafone MiFi 2352 -ATTRS{idVendor}=="1410", ATTRS{idProduct}=="5041", RUN+="usb_modeswitch '%b/%k'" - -# Sequans SQN1210/SQN1220 (generic chipsets) -ATTRS{idVendor}=="148e", ATTRS{idProduct}=="a000", RUN+="usb_modeswitch '%b/%k'" - -# Motorola 802.11 bg WLAN (TER/GUSB3-E) -ATTRS{idVendor}=="148f", ATTRS{idProduct}=="2578", RUN+="usb_modeswitch '%b/%k'" - -# C-motech CHU-628S -ATTRS{idVendor}=="16d8", ATTRS{idProduct}=="6281", RUN+="usb_modeswitch '%b/%k'" - -# C-motech D-50 (aka "CDU-680") -ATTRS{idVendor}=="16d8", ATTRS{idProduct}=="6803", RUN+="usb_modeswitch '%b/%k'" - -# C-motech CHU-629S -ATTRS{idVendor}=="16d8", ATTRS{idProduct}=="700a", RUN+="usb_modeswitch '%b/%k'" - -# C-motech CHU-629S (Variant) -ATTRS{idVendor}=="16d8", ATTRS{idProduct}=="700b", RUN+="usb_modeswitch '%b/%k'" - -# C-motech CGU-628 (aka "Franklin Wireless CGU-628A" aka "4G Systems XS Stick W12") -ATTRS{idVendor}=="16d8", ATTRS{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" - -# JOA Telecom LM-700r -ATTRS{idVendor}=="198a", ATTRS{idProduct}=="0003", RUN+="usb_modeswitch '%b/%k'" - -# Beceem BCSM250 -ATTRS{idVendor}=="198f", ATTRS{idProduct}=="bccd", RUN+="usb_modeswitch '%b/%k'" - -# ZTE MU351 -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0003", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone/ZTE K3806-Z -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0013", RUN+="usb_modeswitch '%b/%k'" - -# ZTE AC581 -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0026", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone (ZTE) K2525 -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0040", RUN+="usb_modeswitch '%b/%k'" - -# ZTE MF110 (Variant) -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0053", RUN+="usb_modeswitch '%b/%k'" - -# ZTE MF110 (Variant) -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0083", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone (ZTE) K4505-Z -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0101", RUN+="usb_modeswitch '%b/%k'" - -# ZTE MF112 -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0103", RUN+="usb_modeswitch '%b/%k'" - -# ZTE MF637 (Variant for Orange France) -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0110", RUN+="usb_modeswitch '%b/%k'" - -# ZTE MF651 -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0115", RUN+="usb_modeswitch '%b/%k'" - -# ZTE MF190 (Variant) -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0149", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone (ZTE) K3805-Z -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1001", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone (ZTE) K3570-Z -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1007", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone (ZTE) K3571-Z -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1009", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone (ZTE) K3806-Z -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1013", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone (ZTE) K4510-Z -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1171", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone (ZTE) K3770-Z -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1175", RUN+="usb_modeswitch '%b/%k'" - -# Vodafone (ZTE) K3772-Z -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1179", RUN+="usb_modeswitch '%b/%k'" - -# ZTE MF691 (T-Mobile Rocket 2.0) -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1201", RUN+="usb_modeswitch '%b/%k'" - -# ZTE MF192 -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1216", RUN+="usb_modeswitch '%b/%k'" - -# ZTE MF190 -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1224", RUN+="usb_modeswitch '%b/%k'" - -# ZTE devices -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="2000", RUN+="usb_modeswitch '%b/%k'" - -# ZTE AX226 WiMax -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="bccd", RUN+="usb_modeswitch '%b/%k'" - -# ZTE "ffe" devices 1 (e.g. Cricket A605) -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="ffe6", RUN+="usb_modeswitch '%b/%k'" - -# ZTE "fff" devices 1 -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="fff5", RUN+="usb_modeswitch '%b/%k'" - -# ZTE "fff" devices 2 -ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="fff6", RUN+="usb_modeswitch '%b/%k'" - -# BandRich BandLuxe C100, C120, C170, C270, C3xx -ATTRS{idVendor}=="1a8d", ATTRS{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" - -# Hummer DTM5731 -ATTRS{idVendor}=="1ab7", ATTRS{idProduct}=="5700", RUN+="usb_modeswitch '%b/%k'" - -# EpiValley SEC-7089 (featured by Alegro and Starcomms / iZAP) -ATTRS{idVendor}=="1b7d", ATTRS{idProduct}=="0700", RUN+="usb_modeswitch '%b/%k'" - -# Alcatel X200/X200L/X060S -ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" - -# Alcatel One Touch X020 -ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="1001", RUN+="usb_modeswitch '%b/%k'" - -# Vibe 3G Modem -ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6061", RUN+="usb_modeswitch '%b/%k'" - -# MyWave SW006 Sport Phone/Modem Combination -ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9200", RUN+="usb_modeswitch '%b/%k'" - -# Longcheer SU9800 -ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9800", RUN+="usb_modeswitch '%b/%k'" - -# BSNL Capitel -ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9e00", RUN+="usb_modeswitch '%b/%k'" - -# MobiData MBD-200HU and others -ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" - -# Solomon S3Gm-660 -ATTRS{idVendor}=="1dd6", ATTRS{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" - -# Option iCON 210, PROLiNK PHS100, Hyundai MB-810, A-Link 3GU -ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" - -# AirPlus MCD-800 -ATTRS{idVendor}=="1edf", ATTRS{idProduct}=="6003", RUN+="usb_modeswitch '%b/%k'" - -# Onda MW833UP -ATTRS{idVendor}=="1ee8", ATTRS{idProduct}=="0009", RUN+="usb_modeswitch '%b/%k'" - -# Onda MW833UP -ATTRS{idVendor}=="1ee8", ATTRS{idProduct}=="0013", RUN+="usb_modeswitch '%b/%k'" - -# Onda MW836UP-K -ATTRS{idVendor}=="1ee8", ATTRS{idProduct}=="0040", RUN+="usb_modeswitch '%b/%k'" - -# Cricket A600 -ATTRS{idVendor}=="1f28", ATTRS{idProduct}=="0021", RUN+="usb_modeswitch '%b/%k'" - -# Franklin Wireless U210 (Variant) -ATTRS{idVendor}=="1fac", ATTRS{idProduct}=="0032", RUN+="usb_modeswitch '%b/%k'" - -# Franklin Wireless U210 -ATTRS{idVendor}=="1fac", ATTRS{idProduct}=="0130", RUN+="usb_modeswitch '%b/%k'" - -# Haier CE 100 -ATTRS{idVendor}=="201e", ATTRS{idProduct}=="2009", RUN+="usb_modeswitch '%b/%k'" - -# SpeedUp SU-8000U -ATTRS{idVendor}=="2020", ATTRS{idProduct}=="f00e", RUN+="usb_modeswitch '%b/%k'" - -# Linktop LW272/LW273 (BSNL Teracom) -ATTRS{idVendor}=="230d", ATTRS{idProduct}=="0001", RUN+="usb_modeswitch '%b/%k'" - -# Visiontek 82GH 3G -ATTRS{idVendor}=="230d", ATTRS{idProduct}=="0007", RUN+="usb_modeswitch '%b/%k'" - -# Exiss Mobile E-190 series (made by C-motech) -ATTRS{idVendor}=="8888", ATTRS{idProduct}=="6500", RUN+="usb_modeswitch '%b/%k'" - -LABEL="modeswitch_rules_end" +# Part of usb-modeswitch-data, version 20130607 +# +# Recommended use with USB_ModeSwitch >= 1.2.5, works with versions >= 1.0.3 +# (New style, interface class check moved to dispatcher) +# +ACTION!="add|change", GOTO="modeswitch_rules_end" + +# Adds a symlink "gsmmodem[n]" to the lowest ttyUSB port with interrupt +# transfer; checked against a list of known modems, or else no action +KERNEL=="ttyUSB*", ATTRS{bNumConfigurations}=="*", PROGRAM="usb_modeswitch --symlink-name %p %s{idVendor} %s{idProduct} %E{PRODUCT}", SYMLINK="%c" + +SUBSYSTEM!="usb", GOTO="modeswitch_rules_end" + +# Adds the device ID to the "option" driver after a warm boot +# in cases when the device is yet unknown to the driver; checked +# against a list of known modems, or else no action +ATTR{bInterfaceClass}=="ff", ATTR{bInterfaceNumber}=="00", ATTRS{bNumConfigurations}=="*", RUN+="usb_modeswitch --driver-bind %p %s{idVendor} %s{idProduct} %E{PRODUCT}" + + +# Don't continue on "change" event, prevent trigger by changed configuration +ACTION!="add", GOTO="modeswitch_rules_end" + + +# HP LaserJet Professional P1102 +ATTR{idVendor}=="03f0", ATTR{idProduct}=="002a", RUN+="usb_modeswitch '%b/%k'" + +# Yota Router (Quanta 1QDLZZZ0ST2) +ATTR{idVendor}=="0408", ATTR{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" + +# Nokia CS-10 +ATTR{idVendor}=="0421", ATTR{idProduct}=="060c", RUN+="usb_modeswitch '%b/%k'" + +# Nokia CS-15 +ATTR{idVendor}=="0421", ATTR{idProduct}=="0610", RUN+="usb_modeswitch '%b/%k'" + +# Nokia CS-12 +ATTR{idVendor}=="0421", ATTR{idProduct}=="0618", RUN+="usb_modeswitch '%b/%k'" + +# Nokia CS-11 +ATTR{idVendor}=="0421", ATTR{idProduct}=="061d", RUN+="usb_modeswitch '%b/%k'" + +# Nokia CS-17 +ATTR{idVendor}=="0421", ATTR{idProduct}=="0622", RUN+="usb_modeswitch '%b/%k'" + +# Nokia CS-18 +ATTR{idVendor}=="0421", ATTR{idProduct}=="0627", RUN+="usb_modeswitch '%b/%k'" + +# Nokia CS-19 +ATTR{idVendor}=="0421", ATTR{idProduct}=="062c", RUN+="usb_modeswitch '%b/%k'" + +# Nokia CS-7M-01 +ATTR{idVendor}=="0421", ATTR{idProduct}=="0632", RUN+="usb_modeswitch '%b/%k'" + +# Nokia CS-21M-02 +ATTR{idVendor}=="0421", ATTR{idProduct}=="0637", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone MD950 (Wisue Technology), Philips (?) +ATTR{idVendor}=="0471", ATTR{idProduct}=="1210", RUN+="usb_modeswitch '%b/%k'" + +# Philips TalkTalk (NXP Semiconductors "Dragonfly") +ATTR{idVendor}=="0471", ATTR{idProduct}=="1237", RUN+="usb_modeswitch '%b/%k'" + +# Kyocera W06K CDMA modem +ATTR{idVendor}=="0482", ATTR{idProduct}=="024d", RUN+="usb_modeswitch '%b/%k'" + +# I-O Data WMX2-U Wimax +ATTR{idVendor}=="04bb", ATTR{idProduct}=="bccd", RUN+="usb_modeswitch '%b/%k'" + +# Alcatel-Lucent T930S +ATTR{idVendor}=="04cc", ATTR{idProduct}=="225c", RUN+="usb_modeswitch '%b/%k'" + +# Samsung GT-B1110 +ATTR{idVendor}=="04e8", ATTR{idProduct}=="680c", RUN+="usb_modeswitch '%b/%k'" + +# Samsung GT-B3730 +ATTR{idVendor}=="04e8", ATTR{idProduct}=="689a", RUN+="usb_modeswitch '%b/%k'" + +# Samsung U209 +ATTR{idVendor}=="04e8", ATTR{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" + +# AVM Fritz!Wlan USB Stick N +ATTR{idVendor}=="057c", ATTR{idProduct}=="84ff", RUN+="usb_modeswitch '%b/%k'" + +# Axesstel Modems (w/ initial idProduct 0x0010) +ATTR{idVendor}=="05c6", ATTR{idProduct}=="0010", RUN+="usb_modeswitch '%b/%k'" + +# Prolink P2000 CDMA, Samsung SGH-Z810, older Option devices, Vertex Wireless 100 Series, AnyDATA devices, Bless UC165, Option GlobeTrotter GX0201, Celot K-300, Celot CT-680, StrongRising device +ATTR{idVendor}=="05c6", ATTR{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" + +# HSDPA USB modem from dealextreme +ATTR{idVendor}=="05c6", ATTR{idProduct}=="2000", RUN+="usb_modeswitch '%b/%k'" + +# D-Link DWM-162-U5, Micromax MMX 300c +ATTR{idVendor}=="05c6", ATTR{idProduct}=="2001", RUN+="usb_modeswitch '%b/%k'" + +# AnyDATA APE-540H +ATTR{idVendor}=="05c6", ATTR{idProduct}=="6503", RUN+="usb_modeswitch '%b/%k'" + +# Siptune LM-75 ("LinuxModem") +ATTR{idVendor}=="05c6", ATTR{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" + +# Qtronix EVDO 3G Modem (for TianYi) +ATTR{idVendor}=="05c7", ATTR{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" + +# InfoCert Business Key (SmartCard/Reader emulation) +ATTR{idVendor}=="072f", ATTR{idProduct}=="100d", RUN+="usb_modeswitch '%b/%k'" + +# D-Link DWM-156 HSUPA 3.75G USB Modem +ATTR{idVendor}=="07d1", ATTR{idProduct}=="a800", RUN+="usb_modeswitch '%b/%k'" + +# D-Link DWM-156 HSUPA 3.75G USB Modem +ATTR{idVendor}=="07d1", ATTR{idProduct}=="a804", RUN+="usb_modeswitch '%b/%k'" + +# Dymo LabelManager +ATTR{idVendor}=="0922", ATTR{idProduct}=="1001", RUN+="usb_modeswitch '%b/%k'" + +# Toshiba G450 +ATTR{idVendor}=="0930", ATTR{idProduct}=="0d46", RUN+="usb_modeswitch '%b/%k'" + +# Zydas ZD1211RW WLAN USB, Sphairon HomeLink 1202 (Variant 1) +ATTR{idVendor}=="0ace", ATTR{idProduct}=="2011", RUN+="usb_modeswitch '%b/%k'" + +# Zydas ZD1211RW WLAN USB, Sphairon HomeLink 1202 (Variant 2) +ATTR{idVendor}=="0ace", ATTR{idProduct}=="20ff", RUN+="usb_modeswitch '%b/%k'" + +# Option iCon 711 +ATTR{idVendor}=="0af0", ATTR{idProduct}=="4007", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="6711", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="6731", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="6751", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="6771", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="6791", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="6811", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="6911", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="6951", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="6971", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7011", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7031", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7051", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7071", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7111", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7211", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7251", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7271", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7301", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7311", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7361", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7381", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7401", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7501", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7601", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7701", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7706", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7801", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7901", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7a01", RUN+="usb_modeswitch '%b/%k'" + +# Option iCon 461 +ATTR{idVendor}=="0af0", ATTR{idProduct}=="7a05", RUN+="usb_modeswitch '%b/%k'" + +# Option Globetrotter (Variant) +ATTR{idVendor}=="0af0", ATTR{idProduct}=="8006", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="8200", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="8201", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="8300", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="8302", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="8304", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="8400", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="8600", RUN+="usb_modeswitch '%b/%k'" + +# Option GI0643 (aka XYFI) +ATTR{idVendor}=="0af0", ATTR{idProduct}=="8700", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="8800", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="8900", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="9000", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="9200", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="c031", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="c100", RUN+="usb_modeswitch '%b/%k'" + +# Option GlobeTrotter GI1515 +ATTR{idVendor}=="0af0", ATTR{idProduct}=="d001", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="d013", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="d031", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="d033", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="d035", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="d055", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="d057", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="d058", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="d155", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="d157", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="d255", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="d257", RUN+="usb_modeswitch '%b/%k'" + +# Option HSO device +ATTR{idVendor}=="0af0", ATTR{idProduct}=="d357", RUN+="usb_modeswitch '%b/%k'" + +# Olivetti Olicard 100 and others +ATTR{idVendor}=="0b3c", ATTR{idProduct}=="c700", RUN+="usb_modeswitch '%b/%k'" + +# Olivetti Olicard 145, 155 +ATTR{idVendor}=="0b3c", ATTR{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" + +# Atheros Wireless / Netgear WNDA3200 +ATTR{idVendor}=="0cf3", ATTR{idProduct}=="20ff", RUN+="usb_modeswitch '%b/%k'" + +# Kobil mIdentity 3G (1) +ATTR{idVendor}=="0d46", ATTR{idProduct}=="45a1", RUN+="usb_modeswitch '%b/%k'" + +# Kobil mIdentity 3G (2) +ATTR{idVendor}=="0d46", ATTR{idProduct}=="45a5", RUN+="usb_modeswitch '%b/%k'" + +# Mobile Action ("Smart Cable") +ATTR{idVendor}=="0df7", ATTR{idProduct}=="0800", RUN+="usb_modeswitch '%b/%k'" + +# MediaTek MT6276M +ATTR{idVendor}=="0e8d", ATTR{idProduct}=="0002", RUN+="usb_modeswitch '%b/%k'" + +# MediaTek Wimax USB Card +ATTR{idVendor}=="0e8d", ATTR{idProduct}=="7109", RUN+="usb_modeswitch '%b/%k'" + +# Sony Ericsson MD300 +ATTR{idVendor}=="0fce", ATTR{idProduct}=="d0cf", RUN+="usb_modeswitch '%b/%k'" + +# Sony Ericsson MD400 +ATTR{idVendor}=="0fce", ATTR{idProduct}=="d0e1", RUN+="usb_modeswitch '%b/%k'" + +# Sony Ericsson MD400G +ATTR{idVendor}=="0fce", ATTR{idProduct}=="d103", RUN+="usb_modeswitch '%b/%k'" + +# GW D301 (Advinne AMC) +ATTR{idVendor}=="0fd1", ATTR{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" + +# LG LDU-1900D EV-DO (Rev. A) +ATTR{idVendor}=="1004", ATTR{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" + +# LG HDM-2100 (EVDO Rev.A USB modem) +ATTR{idVendor}=="1004", ATTR{idProduct}=="607f", RUN+="usb_modeswitch '%b/%k'" + +# LG L-05A +ATTR{idVendor}=="1004", ATTR{idProduct}=="613a", RUN+="usb_modeswitch '%b/%k'" + +# LG LUU-2100TI (aka AT&T USBConnect Turbo) +ATTR{idVendor}=="1004", ATTR{idProduct}=="613f", RUN+="usb_modeswitch '%b/%k'" + +# LG L-07A +ATTR{idVendor}=="1004", ATTR{idProduct}=="614e", RUN+="usb_modeswitch '%b/%k'" + +# LG LUU-2110TI +ATTR{idVendor}=="1004", ATTR{idProduct}=="6156", RUN+="usb_modeswitch '%b/%k'" + +# LG AD600 +ATTR{idVendor}=="1004", ATTR{idProduct}=="6190", RUN+="usb_modeswitch '%b/%k'" + +# LG VL600 +ATTR{idVendor}=="1004", ATTR{idProduct}=="61aa", RUN+="usb_modeswitch '%b/%k'" + +# LG L-02C LTE +ATTR{idVendor}=="1004", ATTR{idProduct}=="61dd", RUN+="usb_modeswitch '%b/%k'" + +# LG SD711 +ATTR{idVendor}=="1004", ATTR{idProduct}=="61e7", RUN+="usb_modeswitch '%b/%k'" + +# LG L-08C (NTT docomo) +ATTR{idVendor}=="1004", ATTR{idProduct}=="61eb", RUN+="usb_modeswitch '%b/%k'" + +# LG L-03D LTE/3G +ATTR{idVendor}=="1004", ATTR{idProduct}=="6327", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E630 +ATTR{idVendor}=="1033", ATTR{idProduct}=="0035", RUN+="usb_modeswitch '%b/%k'" + +# UTStarcom UM175 (distributor "Alltel") +ATTR{idVendor}=="106c", ATTR{idProduct}=="3b03", RUN+="usb_modeswitch '%b/%k'" + +# Pantech / UTStarcom UMW190 (Verizon) +ATTR{idVendor}=="106c", ATTR{idProduct}=="3b05", RUN+="usb_modeswitch '%b/%k'" + +# UTStarcom UM185E (distributor "Alltel") +ATTR{idVendor}=="106c", ATTR{idProduct}=="3b06", RUN+="usb_modeswitch '%b/%k'" + +# Pantech UML290 +ATTR{idVendor}=="106c", ATTR{idProduct}=="3b11", RUN+="usb_modeswitch '%b/%k'" + +# Option Beemo / Pantech P4200 LTE +ATTR{idVendor}=="106c", ATTR{idProduct}=="3b14", RUN+="usb_modeswitch '%b/%k'" + +# Sagem F@ST 9520-35-GLR +ATTR{idVendor}=="1076", ATTR{idProduct}=="7f40", RUN+="usb_modeswitch '%b/%k'" + +# Hisense E910 EVDO Phone +ATTR{idVendor}=="109b", ATTR{idProduct}=="f009", RUN+="usb_modeswitch '%b/%k'" + +# Sierra devices (specific driver) +ATTR{idVendor}=="1199", ATTR{idProduct}=="0fff", RUN+="usb_modeswitch '%b/%k'" + +# Digicom 8E4455 (and all Pirelli devices - EXPERIMENTAL) +ATTR{idVendor}=="1266", ATTR{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E169 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1001", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E220, E230, E270, E870 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1003", RUN+="usb_modeswitch '%b/%k'" + +# Huawei V725 Phone (aka Vodafone 725) +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1009", RUN+="usb_modeswitch '%b/%k'" + +# Huawei U7510 / U7517 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="101e", RUN+="usb_modeswitch '%b/%k'" + +# Huawei U8220, T-Mobile Pulse (Android smartphone) +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1030", RUN+="usb_modeswitch '%b/%k'" + +# Huawei U8110 / Joy, Vodafone 845 (Android smartphone) +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1031", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E180 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1414", RUN+="usb_modeswitch '%b/%k'" + +# Huawei, newer modems +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1446", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E352 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1449", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone (Huawei) K3806 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="14ad", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E173 (Viettel 3G) +ATTR{idVendor}=="12d1", ATTR{idProduct}=="14b5", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone (Huawei) K4511 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="14b7", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E173u-2, E177 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="14ba", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone (Huawei) K4605 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="14c1", RUN+="usb_modeswitch '%b/%k'" + +# K5005 Vodafone/Huawei +ATTR{idVendor}=="12d1", ATTR{idProduct}=="14c3", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone/Huawei K3771 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="14c4", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone (Huawei) K4510 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="14c5", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone / Huawei K3770 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="14d1", RUN+="usb_modeswitch '%b/%k'" + +# T-Mobile NL (Huawei E352) +ATTR{idVendor}=="12d1", ATTR{idProduct}=="14fe", RUN+="usb_modeswitch '%b/%k'" + +# Huawei EC156, Huawei E372u-8 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1505", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E392u-12, E3131 (Variant) +ATTR{idVendor}=="12d1", ATTR{idProduct}=="151a", RUN+="usb_modeswitch '%b/%k'" + +# Huawei K3765 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1520", RUN+="usb_modeswitch '%b/%k'" + +# Huawei K4505 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1521", RUN+="usb_modeswitch '%b/%k'" + +# Huawei R201 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1523", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone / Huawei K3772 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1526", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E1553 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1553", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E173 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1557", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E171 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="155b", RUN+="usb_modeswitch '%b/%k'" + +# Huawei U2800 Phone +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1805", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E173s +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1c0b", RUN+="usb_modeswitch '%b/%k'" + +# Huawei GP02 (E587 Variant) +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1c1b", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E173 (Moviestar) +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1c24", RUN+="usb_modeswitch '%b/%k'" + +# Huawei ET302 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1d50", RUN+="usb_modeswitch '%b/%k'" + +# Huawei ET8282, Huawei ET127 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1da1", RUN+="usb_modeswitch '%b/%k'" + +# Huawei E353 (3.se) +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1f01", RUN+="usb_modeswitch '%b/%k'" + +# KDDI (Huawei) HWD12 LTE +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1f03", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone / Huawei K3773 +ATTR{idVendor}=="12d1", ATTR{idProduct}=="1f11", RUN+="usb_modeswitch '%b/%k'" + +# Huawei BM358 WiMAX +ATTR{idVendor}=="12d1", ATTR{idProduct}=="380b", RUN+="usb_modeswitch '%b/%k'" + +# Cisco AM10 "Valet Connector" +ATTR{idVendor}=="1307", ATTR{idProduct}=="1169", RUN+="usb_modeswitch '%b/%k'" + +# Novatel Wireless devices +ATTR{idVendor}=="1410", ATTR{idProduct}=="5010", RUN+="usb_modeswitch '%b/%k'" + +# Novatel MC990D +ATTR{idVendor}=="1410", ATTR{idProduct}=="5020", RUN+="usb_modeswitch '%b/%k'" + +# Novatel MC996D +ATTR{idVendor}=="1410", ATTR{idProduct}=="5023", RUN+="usb_modeswitch '%b/%k'" + +# Novatel U760 +ATTR{idVendor}=="1410", ATTR{idProduct}=="5030", RUN+="usb_modeswitch '%b/%k'" + +# Novatel MC760 3G +ATTR{idVendor}=="1410", ATTR{idProduct}=="5031", RUN+="usb_modeswitch '%b/%k'" + +# Novatel Generic MiFi 2352 / Vodafone MiFi 2352 +ATTR{idVendor}=="1410", ATTR{idProduct}=="5041", RUN+="usb_modeswitch '%b/%k'" + +# Novatel Wireless MC545 HSPA, U679 LTE +ATTR{idVendor}=="1410", ATTR{idProduct}=="5059", RUN+="usb_modeswitch '%b/%k'" + +# Novatel Generic MiFi 2372 / Vodafone MiFi 2372 +ATTR{idVendor}=="1410", ATTR{idProduct}=="7001", RUN+="usb_modeswitch '%b/%k'" + +# Sequans SQN1210/SQN1220 (generic chipsets) +ATTR{idVendor}=="148e", ATTR{idProduct}=="a000", RUN+="usb_modeswitch '%b/%k'" + +# Motorola 802.11 bg WLAN (TER/GUSB3-E) +ATTR{idVendor}=="148f", ATTR{idProduct}=="2578", RUN+="usb_modeswitch '%b/%k'" + +# China TeleCom CBP7.0 +ATTR{idVendor}=="15eb", ATTR{idProduct}=="7153", RUN+="usb_modeswitch '%b/%k'" + +# C-motech CHU-628S +ATTR{idVendor}=="16d8", ATTR{idProduct}=="6281", RUN+="usb_modeswitch '%b/%k'" + +# C-motech D-50 (aka "CDU-680", "CNU-680") +ATTR{idVendor}=="16d8", ATTR{idProduct}=="6803", RUN+="usb_modeswitch '%b/%k'" + +# C-motech CDU-685a +ATTR{idVendor}=="16d8", ATTR{idProduct}=="6804", RUN+="usb_modeswitch '%b/%k'" + +# C-motech CHU-629S +ATTR{idVendor}=="16d8", ATTR{idProduct}=="700a", RUN+="usb_modeswitch '%b/%k'" + +# C-motech CHU-629S (Variant) +ATTR{idVendor}=="16d8", ATTR{idProduct}=="700b", RUN+="usb_modeswitch '%b/%k'" + +# C-motech CGU-628 (aka "Franklin Wireless CGU-628A" aka "4G Systems XS Stick W12") +ATTR{idVendor}=="16d8", ATTR{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" + +# Axesstel MU130 +ATTR{idVendor}=="1726", ATTR{idProduct}=="f00e", RUN+="usb_modeswitch '%b/%k'" + +# JOA Telecom LM-700r +ATTR{idVendor}=="198a", ATTR{idProduct}=="0003", RUN+="usb_modeswitch '%b/%k'" + +# Beceem BCSM250 +ATTR{idVendor}=="198f", ATTR{idProduct}=="bccd", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MU351 +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0003", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone/ZTE K3806-Z +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0013", RUN+="usb_modeswitch '%b/%k'" + +# ZTE AC581 +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0026", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF637 +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0031", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone (ZTE) K2525 +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0040", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF110 (Variant) +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0053", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF110 (Variant), ZTE MF110 (Variant) +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0083", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone (ZTE) K4505-Z +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0101", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF112 +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0103", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF637 (Variant for Orange France) +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0110", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF651 +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0115", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF652 (Variant) +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0146", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF190 (Variant) +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0149", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF656A, MF668A, MF669 +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0150", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF190 (Variant) and others +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0154", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF820 4G LTE +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0166", RUN+="usb_modeswitch '%b/%k'" + +# ZTE A371B +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0169", RUN+="usb_modeswitch '%b/%k'" + +# Onda MT8205 LTE +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0266", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF821D +ATTR{idVendor}=="19d2", ATTR{idProduct}=="0325", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone (ZTE) K3805-Z +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1001", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone (ZTE) K3570-Z +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1007", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone (ZTE) K3571-Z +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1009", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone (ZTE) K3806-Z +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1013", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone K5006Z (MF821) +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1017", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone (ZTE) K4510-Z +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1171", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone / ZTE K3770-Z +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1175", RUN+="usb_modeswitch '%b/%k'" + +# Vodafone (ZTE) K3772-Z +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1179", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF691 (T-Mobile Rocket 2.0) +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1201", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF192+ +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1216", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF190 +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1224", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF680 +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1227", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF192 (yet annother variant) +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1514", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF192 (Variant) +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1517", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF652 +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1520", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF591 TMobile +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1523", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF196 +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1528", RUN+="usb_modeswitch '%b/%k'" + +# ZTE MF190J +ATTR{idVendor}=="19d2", ATTR{idProduct}=="1542", RUN+="usb_modeswitch '%b/%k'" + +# ZTE devices +ATTR{idVendor}=="19d2", ATTR{idProduct}=="2000", RUN+="usb_modeswitch '%b/%k'" + +# ZTE AX226 WiMax +ATTR{idVendor}=="19d2", ATTR{idProduct}=="bccd", RUN+="usb_modeswitch '%b/%k'" + +# ZTE AC682 (a.k.a. SmartFren Connex) +ATTR{idVendor}=="19d2", ATTR{idProduct}=="ffde", RUN+="usb_modeswitch '%b/%k'" + +# ZTE "ffe" devices 1 (e.g. Cricket A605) +ATTR{idVendor}=="19d2", ATTR{idProduct}=="ffe6", RUN+="usb_modeswitch '%b/%k'" + +# ZTE "fff" devices 1 +ATTR{idVendor}=="19d2", ATTR{idProduct}=="fff5", RUN+="usb_modeswitch '%b/%k'" + +# ZTE "fff" devices 2 +ATTR{idVendor}=="19d2", ATTR{idProduct}=="fff6", RUN+="usb_modeswitch '%b/%k'" + +# BandRich BandLuxe C100, C120, C170, C270, C3xx, C508 +ATTR{idVendor}=="1a8d", ATTR{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" + +# BandRich BandLuxe C339 +ATTR{idVendor}=="1a8d", ATTR{idProduct}=="2000", RUN+="usb_modeswitch '%b/%k'" + +# Hummer DTM5731, Aircard 901 +ATTR{idVendor}=="1ab7", ATTR{idProduct}=="5700", RUN+="usb_modeswitch '%b/%k'" + +# EpiValley SEC-7089 (featured by Alegro and Starcomms / iZAP) +ATTR{idVendor}=="1b7d", ATTR{idProduct}=="0700", RUN+="usb_modeswitch '%b/%k'" + +# ETCOM E300 +ATTR{idVendor}=="1bbb", ATTR{idProduct}=="000f", RUN+="usb_modeswitch '%b/%k'" + +# Alcatel OT-X080C +ATTR{idVendor}=="1bbb", ATTR{idProduct}=="00ca", RUN+="usb_modeswitch '%b/%k'" + +# Alcatel X200/X200L/X060S, Archos G9 3G Key +ATTR{idVendor}=="1bbb", ATTR{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" + +# Alcatel OT-X220D, L100V +ATTR{idVendor}=="1bbb", ATTR{idProduct}=="f017", RUN+="usb_modeswitch '%b/%k'" + +# Alcatel OT X220L +ATTR{idVendor}=="1bbb", ATTR{idProduct}=="f052", RUN+="usb_modeswitch '%b/%k'" + +# Alcatel One Touch X020 +ATTR{idVendor}=="1c9e", ATTR{idProduct}=="1001", RUN+="usb_modeswitch '%b/%k'" + +# Vibe 3G Modem +ATTR{idVendor}=="1c9e", ATTR{idProduct}=="6061", RUN+="usb_modeswitch '%b/%k'" + +# MyWave SW006 Sport Phone/Modem Combination +ATTR{idVendor}=="1c9e", ATTR{idProduct}=="9200", RUN+="usb_modeswitch '%b/%k'" + +# Longcheer SU9800 +ATTR{idVendor}=="1c9e", ATTR{idProduct}=="9800", RUN+="usb_modeswitch '%b/%k'" + +# Telewell TW-3G HSPA+ +ATTR{idVendor}=="1c9e", ATTR{idProduct}=="98ff", RUN+="usb_modeswitch '%b/%k'" + +# BSNL Capitel +ATTR{idVendor}=="1c9e", ATTR{idProduct}=="9e00", RUN+="usb_modeswitch '%b/%k'" + +# Explay Slim +ATTR{idVendor}=="1c9e", ATTR{idProduct}=="9e08", RUN+="usb_modeswitch '%b/%k'" + +# MobiData MBD-200HU and others +ATTR{idVendor}=="1c9e", ATTR{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" + +# Qisda H21 Flying Beetle +ATTR{idVendor}=="1da5", ATTR{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" + +# Solomon S3Gm-660 +ATTR{idVendor}=="1dd6", ATTR{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" + +# Philips PicoPix 1020 Projector +ATTR{idVendor}=="1de1", ATTR{idProduct}=="1101", RUN+="usb_modeswitch '%b/%k'" + +# Option iCON 210, PROLiNK PHS100, Hyundai MB-810, A-Link 3GU +ATTR{idVendor}=="1e0e", ATTR{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" + +# 3GO 3GO11 HSUPA +ATTR{idVendor}=="1e89", ATTR{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" + +# AirPlus MCD-800 +ATTR{idVendor}=="1edf", ATTR{idProduct}=="6003", RUN+="usb_modeswitch '%b/%k'" + +# Onda MW833UP +ATTR{idVendor}=="1ee8", ATTR{idProduct}=="0009", RUN+="usb_modeswitch '%b/%k'" + +# Onda MW833UP +ATTR{idVendor}=="1ee8", ATTR{idProduct}=="0013", RUN+="usb_modeswitch '%b/%k'" + +# Onda MW836UP-K +ATTR{idVendor}=="1ee8", ATTR{idProduct}=="0040", RUN+="usb_modeswitch '%b/%k'" + +# Onda MDC655 +ATTR{idVendor}=="1ee8", ATTR{idProduct}=="004a", RUN+="usb_modeswitch '%b/%k'" + +# Onda MW875UP +ATTR{idVendor}=="1ee8", ATTR{idProduct}=="0054", RUN+="usb_modeswitch '%b/%k'" + +# Onda MSA 14.4 (TIM Brasil) +ATTR{idVendor}=="1ee8", ATTR{idProduct}=="0060", RUN+="usb_modeswitch '%b/%k'" + +# Onda TM201 14.4 (TIM Italy) +ATTR{idVendor}=="1ee8", ATTR{idProduct}=="0063", RUN+="usb_modeswitch '%b/%k'" + +# Onda WM301 +ATTR{idVendor}=="1ee8", ATTR{idProduct}=="0068", RUN+="usb_modeswitch '%b/%k'" + +# Cricket A600 +ATTR{idVendor}=="1f28", ATTR{idProduct}=="0021", RUN+="usb_modeswitch '%b/%k'" + +# Franklin Wireless U210 (Variant) +ATTR{idVendor}=="1fac", ATTR{idProduct}=="0032", RUN+="usb_modeswitch '%b/%k'" + +# Franklin Wireless U210 +ATTR{idVendor}=="1fac", ATTR{idProduct}=="0130", RUN+="usb_modeswitch '%b/%k'" + +# Franklin Wireless U600 +ATTR{idVendor}=="1fac", ATTR{idProduct}=="0150", RUN+="usb_modeswitch '%b/%k'" + +# Franklin Wireless U600 +ATTR{idVendor}=="1fac", ATTR{idProduct}=="0151", RUN+="usb_modeswitch '%b/%k'" + +# D-Link DWM-156 (Variant) +ATTR{idVendor}=="2001", ATTR{idProduct}=="a706", RUN+="usb_modeswitch '%b/%k'" + +# D-Link DWM-156 (Variant) +ATTR{idVendor}=="2001", ATTR{idProduct}=="a707", RUN+="usb_modeswitch '%b/%k'" + +# D-Link DWM-156 (Variant) +ATTR{idVendor}=="2001", ATTR{idProduct}=="a708", RUN+="usb_modeswitch '%b/%k'" + +# D-Link DWR-510 +ATTR{idVendor}=="2001", ATTR{idProduct}=="a805", RUN+="usb_modeswitch '%b/%k'" + +# D-Link DWM-156 (Variant) +ATTR{idVendor}=="2001", ATTR{idProduct}=="a80b", RUN+="usb_modeswitch '%b/%k'" + +# Haier CE682 (EVDO) +ATTR{idVendor}=="201e", ATTR{idProduct}=="1023", RUN+="usb_modeswitch '%b/%k'" + +# Haier CE 100 +ATTR{idVendor}=="201e", ATTR{idProduct}=="2009", RUN+="usb_modeswitch '%b/%k'" + +# Mediatek MT6229 +ATTR{idVendor}=="2020", ATTR{idProduct}=="0002", RUN+="usb_modeswitch '%b/%k'" + +# SpeedUp SU-8000U +ATTR{idVendor}=="2020", ATTR{idProduct}=="f00e", RUN+="usb_modeswitch '%b/%k'" + +# Changhong CH690 +ATTR{idVendor}=="2077", ATTR{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" + +# Telenet 3G dongle (T&W WU160) +ATTR{idVendor}=="2077", ATTR{idProduct}=="f000", RUN+="usb_modeswitch '%b/%k'" + +# StrongRising (China Telcom), Air FlexiNet +ATTR{idVendor}=="21f5", ATTR{idProduct}=="1000", RUN+="usb_modeswitch '%b/%k'" + +# WeTelecom WM-D200 +ATTR{idVendor}=="22de", ATTR{idProduct}=="6801", RUN+="usb_modeswitch '%b/%k'" + +# WeTelecom WM-D300 +ATTR{idVendor}=="22de", ATTR{idProduct}=="6803", RUN+="usb_modeswitch '%b/%k'" + +# Tata Photon+ +ATTR{idVendor}=="22f4", ATTR{idProduct}=="0021", RUN+="usb_modeswitch '%b/%k'" + +# Linktop LW272/LW273 (BSNL Teracom) +ATTR{idVendor}=="230d", ATTR{idProduct}=="0001", RUN+="usb_modeswitch '%b/%k'" + +# Visiontek 82GH 3G +ATTR{idVendor}=="230d", ATTR{idProduct}=="0007", RUN+="usb_modeswitch '%b/%k'" + +# Linktop LW272/LW273 +ATTR{idVendor}=="230d", ATTR{idProduct}=="0101", RUN+="usb_modeswitch '%b/%k'" + +# TP-Link MA180 +ATTR{idVendor}=="2357", ATTR{idProduct}=="0200", RUN+="usb_modeswitch '%b/%k'" + +# Exiss Mobile E-190 series (made by C-motech) +ATTR{idVendor}=="8888", ATTR{idProduct}=="6500", RUN+="usb_modeswitch '%b/%k'" + +LABEL="modeswitch_rules_end" diff --git a/release/src/router/usbmodeswitch/data/ChangeLog b/release/src/router/usbmodeswitch/data/ChangeLog index faa69c6744..caf90f31a7 100644 --- a/release/src/router/usbmodeswitch/data/ChangeLog +++ b/release/src/router/usbmodeswitch/data/ChangeLog @@ -1,3 +1,65 @@ +20130607: + Fixed wrong message content for Huawei U2800 (thanks to Peter Fedorow) +20130602: + Added devices: Nokia CS-12, Alcatel-Lucent T930S, Axesstel MV241, + Pantech UML290, Huawei E3131 (Variant), Huawei U2800 Phone, Huawei GP02 + (E587 Variant), KDDI (Huawei) HWD12 LTE, Novatel MC996D, Axesstel MU130, + ZTE MF656A, MF668A, MF669, Onda MT8205 LTE, ZTE MF680, ZTE MF196, + BandRich BandLuxe C508, Alcatel L100V, Explay Slim, Onda TM201 14.4 + (TIM Italy), Onda WM301, D-Link DWM-156 (several new variants), D-Link + DWR-510, Mediatek MT6229, Changhong CH690, WeTelecom WM-D200; several + new target IDs for Sierra, Huawei and ZTE; fix for WeTelecom WM-D300; + included simple TCL script to regenerate rules file from configs +20121109: + Added devices: Nokia CS-7M-01, LG LUU-2110TI, LG VL600, LG L-03D LTE/3G, + Option Beemo/Pantech P4200 LTE, Vodafone/Huawei K3772, Vodafone/Huawei + K3773, Huawei E171, Huawei E173s, Huawei E177, Huawei EC156, Huawei + E372u-8, Huawei E392u-12, Huawei ET8282, Huawei ET127, ZTE-T A356, + ZTE MF192+, ZTE MF190J, Vodafone/ZTE K3770-Z, T-Mobile/ZTE MF591, + Aircard 901, ETCOM E300, Archos G9 3G Key, Onda MW833UP, Onda MW836UP-K, + Onda MW875UP, Franklin Wireless U600 (Thanks for the contribution of most + new devices goes to Nicolas Carrier from the Parrot company); + Preparation for future MBIM devices: new "NoMBIMCheck" config parameter, + change in modem rules (match devices, not interfaces), best used with + usb_modeswitch 1.2.5 and up +20120815: + Small target ID addition for Huawei E352 +20120812: + Added devices: ZTE MF60, Vodafone K5006Z, Alcatel OT-X220D, + Alcatel OT-X080C, TP-Link MA180, ZTE MF821D, Dymo LabelManager PnP, + Onda MDC655, Option GI0643, WeTelecom WM-D300, China TeleCom CBP7.0, + Linktop LW272/LW273, Nokia CS-11, ZTE MF190 (yet annother variant), + Huawei E535; updated config for C-motech D-50 (16d8:6803); changes + in "rules" file (interface number filters) to reflect recent reports; + fix config for Novatel MC545 (1410:5059) +20120531: + Small but important fix for ZTE Blade phone +20120529: + Added devices: Telenet 3G dongle (T&W WU160), Nokia CS-21M-02, + Telewell TW-3G HSPA+, ZTE MF637, TW-3G HSPA+, Samsung GT-B1110, + ZTE MF192 (yet annother variant), MediaTek MT6276M, Tata Photon+, + Option Globetrotter (Variant), Option iCon 711, Celot K-300, + Hisense E910 EVDO Phone, Yota Router (Quanta 1QDLZZZ0ST2), + K5005 Vodafone/Huawei, D-Link DWM-156 (Variant), Huawei E173 + (Moviestar), Onda MSA 14.4 (TIM Brasil); some target product ID + additions; skip driver loading for some devices now supported by + dedicated drivers; minor fine tuning and corrections +20120120: + Added devices: Air FlexiNet/StrongRising, BandLuxe C339, Celot CT-680, + Huawei E353, Haier CE682, LG L-02C LTE, LG SD711, LG L-08C, LG L-07A, + I-O Data WMX2-U, Option GI1515, ZTE A371B, ZTE MF652 (two Variants); + reduced redundancies in parameters, resulting in smaller files; + ATTENTION !! Needs program version >= 1.2.1 !! +20111023: + Added devices: C-motech CDU-685a, Qisda H21 "Flying Beetle" (needs + program version >= 1.2.0) +20111012: + Added devices: AnyDATA APE-540H, ZTE MF192 (new variant) +20111007: + Added devices: Alcatel OT X220L, Nokia CS-19, Huawei ET302, 3GO 3GO11 + HSUPA, Novatel MC545, ZTE AC682, ZTE MF820 4G LTE, Philips PicoPix 1020 + (LED projector); added some ZTE target IDs; modded udev rules to include + "change" as trigger event 20110805: Added devices: Vodafone/Huawei K4510 & K4511, Vodafone/ZTE K3770-Z, K3772-Z and K4510-Z (thanks to Andrew Bird); five Option HSO modems; diff --git a/release/src/router/usbmodeswitch/data/Makefile b/release/src/router/usbmodeswitch/data/Makefile index 98c62e57e4..2ddd2e0ace 100644 --- a/release/src/router/usbmodeswitch/data/Makefile +++ b/release/src/router/usbmodeswitch/data/Makefile @@ -1,5 +1,5 @@ PROG = usb-modeswitch-data -VERS = 20110805 +VERS = 20130607 RM = /bin/rm -f PREFIX = $(DESTDIR)/usr ETCDIR = $(DESTDIR)/etc @@ -8,29 +8,36 @@ RULESDIR = $(DESTDIR)/lib/udev/rules.d .PHONY: clean -all: +all: 40-usb_modeswitch.rules clean: + $(RM) 40-usb_modeswitch.rules -install: files-install db-install rules-reload +install: all files-install db-install rules-reload install-packed: files-install db-install-packed rules-reload -files-install: +files-install: install -d $(PREFIX)/share/usb_modeswitch install -d $(ETCDIR)/usb_modeswitch.d install -D --mode=644 40-usb_modeswitch.rules $(RULESDIR)/40-usb_modeswitch.rules -db-install: +40-usb_modeswitch.rules: + ./gen-rules.tcl + +db-install: files-install install --mode=644 -t $(PREFIX)/share/usb_modeswitch ./usb_modeswitch.d/* db-install-packed: - cd ./usb_modeswitch.d; tar -czf ../configPack.tar.gz * + @# Create a compressed tar without gzip timestamp, so tar.gz + @# differs only if content is different + cd ./usb_modeswitch.d; tar -cf ../configPack.tar * + gzip -f9n ./configPack.tar install --mode=644 -t $(PREFIX)/share/usb_modeswitch ./configPack.tar.gz rm -f ./configPack.tar.gz rules-reload: - if [ -f $(ETCDIR)/issue ]; then \ + @if [ -f $(ETCDIR)/issue ]; then \ UDEVADM=`which udevadm 2>/dev/null`; \ if [ "x$$UDEVADM" != "x" ]; then \ UDEVADM_VER=`$$UDEVADM -V 2>/dev/null`; \ diff --git a/release/src/router/usbmodeswitch/data/README b/release/src/router/usbmodeswitch/data/README index 24f7f8a2c2..81c510a54b 100644 --- a/release/src/router/usbmodeswitch/data/README +++ b/release/src/router/usbmodeswitch/data/README @@ -12,9 +12,13 @@ This is a data package to complement the full release package of USB_ModeSwitch, a tool to switch multi-mode USB devices. It is used since usb-modeswitch version 1.0.3. -Recommended version of usb-modeswitch package is 1.1.4 and above. Some -devices contained here may not be switched with older versions, even if -they will not cause any error. +Recommended version of usb-modeswitch package is 1.2.5 and above. From +this version on, the rules file does not check for the class of inter- +face 0 anymore, the check was moved to the dispatcher. +The changes were necessary for future MBIM device support. + +Just do "make install". To re-generate udev rules file, type "make clean" +and install again ("tcl" package required). Tasks initiated by the rules file: @@ -24,15 +28,17 @@ Tasks initiated by the rules file: (starting from version 1.1.4 at boot time too) - adding symlink "gsmmodem" to appropriate connection port (out of 2-5 - ports provided by some devices); this is only a help for handling and - NOT needed for switching to work. - This action will be done for known modems only (at least switched once - on this system) + ports provided by some devices); this is only a convenience feature and + NOT needed for switching or later port operation. + This action will be triggered for known modems only (at least switched + once on this system) - loading and binding the "option" driver if the device arrives in modem - state (not CDC/ACM!) and the device ID is not yet known to the driver. - This action will be done for known modems only (at least switched once - on this system) + state (not CDC/ACM) and the device ID is not yet known to the driver. + This action will be triggered only for modems known to the system (at + least switched once). + To prevent the default driver binding, add NoDriverLoading=1 to the + respective device configuration file Explanation about config file names: @@ -62,10 +68,6 @@ Example: ::uPr=CDMA_Mo will match iProduct "CDMA Modem" or "MyCDMA_Mordor" -If information is missing about ambiguous devices, a ":?" is attached -to the file/ID in question. Please contact the forum if you need help -in such a case. - Parameters used only in config files: ===================================== @@ -86,28 +88,19 @@ DriverIDPath= Notes about specific devices: ============================= -- 16d8:6803 (C-motech D50, CDU-680): two versions popped up; one with - ID unchanged, target class 0xff, providing standard serial ports, - and one with target product 0x680a, providing an ACM port. There is - not enough information how to tell them apart; if you have the second - one, we badly need information (usb_modeswitch output with SCSI inquiry) - - 1004:607f (LG KP500): switching message derived from report but untested - 1266:1000 (Royaltek Q110): switching method very likely but unconfirmed -- 1a8d:1000 (BandLuxe): now just one config for all devices; the new parameter - "ReleaseDelay" will improve ambiguous behaviour on older systems - -- Some WiMAX and LTE devices are included since 20101222; for most of these - there are no working Linux drivers available yet. Efforts are underway. +- Some WiMAX and LTE devices are included since 20101222; for some of these + drivers are work in progress or not available yet. Efforts are underway. Stand by or search for "staging drivers". - 05c6:2000 (unnamed HSDPA modem from "dealextreme"): unusually long switching - time, even with MS Windoze - 30 to 40 seconds. Don't be alarmed. + time reported, even with MS Windoze - 30 to 40 seconds. Don't be alarmed. - 0471:1210 (Philips chipset): had to be split after variants with different - targets and behaviour appeared. This is not properly tested yet, please + targets and behaviour appeared. This is not fully confirmed yet, please report problems. @@ -130,4 +123,4 @@ Or find it as the file COPYING in this folder. -Last revised: 2011-07-14, Josua Dietze +Last revised: 2013-06-02, Josua Dietze diff --git a/release/src/router/usbmodeswitch/data/gen-rules.tcl b/release/src/router/usbmodeswitch/data/gen-rules.tcl new file mode 100755 index 0000000000..7e9bdd93a4 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/gen-rules.tcl @@ -0,0 +1,88 @@ +#!/usr/bin/env tclsh +# +# Usage: gen-rules.tcl [--set-version ] +# +# should be in the form YYYYMMDD +# +# A config file is expected to have one comment line containing +# a model name or other concise device specifications + + +# Default version string +set version "20130607" + +if {[lindex $argv 0] == "--set-version" && [regexp {\d\d\d\d\d\d\d\d} [lindex $argv 1]]} { + set version [lindex $argv 1] +} + +set template {ATTR{idVendor}=="+##+", ATTR{idProduct}=="#++#", RUN+="usb_modeswitch '%b/%k'"} + +if {![file isdirectory usb_modeswitch.d]} { + puts "No \"usb_modeswitch.d\" subfolder found" + exit +} + +set filelist [glob -nocomplain ./usb_modeswitch.d/*] +if {[llength $filelist] == 0} { + puts "The \"usb_modeswitch.d\" subfolder is empty" + exit +} + +set wc [open "40-usb_modeswitch.rules" w] + +# Writing file header with given version + +puts -nonewline $wc {# Part of usb-modeswitch-data, version } +puts $wc $version +puts $wc {# +# Recommended use with USB_ModeSwitch >= 1.2.5, works with versions >= 1.0.3 +# (New style, interface class check moved to dispatcher) +# +ACTION!="add|change", GOTO="modeswitch_rules_end" + +# Adds a symlink "gsmmodem[n]" to the lowest ttyUSB port with interrupt +# transfer; checked against a list of known modems, or else no action +KERNEL=="ttyUSB*", ATTRS{bNumConfigurations}=="*", PROGRAM="usb_modeswitch --symlink-name %p %s{idVendor} %s{idProduct} %E{PRODUCT}", SYMLINK="%c" + +SUBSYSTEM!="usb", GOTO="modeswitch_rules_end" + +# Adds the device ID to the "option" driver after a warm boot +# in cases when the device is yet unknown to the driver; checked +# against a list of known modems, or else no action +ATTR{bInterfaceClass}=="ff", ATTR{bInterfaceNumber}=="00", ATTRS{bNumConfigurations}=="*", RUN+="usb_modeswitch --driver-bind %p %s{idVendor} %s{idProduct} %E{PRODUCT}" + + +# Don't continue on "change" event, prevent trigger by changed configuration +ACTION!="add", GOTO="modeswitch_rules_end" +} + +set vendorList "" +set dvid "" + +foreach idfile $filelist { + if {![regexp -nocase {./([0-9A-F]{4}:[0-9A-F]{4})} $idfile d id]} {continue} + if [info exists entry($id)] { + append entry($id) ", " + } + set rc [open $idfile r] + set buffer [read $rc] + close $rc + foreach line [split $buffer \n] { + set comment {} + regexp {# (.*)} $line d comment + if {[string length $comment] > 0} { + append entry($id) [string trim $comment] + break + } + } +} +foreach id_entry [lsort [array names entry]] { + set id [split $id_entry :] + set rule [regsub {\+##\+} $template [lindex $id 0]] + set rule [regsub {#\+\+#} $rule [lindex $id 1]] + puts $wc "\n# $entry($id_entry)\n$rule" +} + +puts $wc { +LABEL="modeswitch_rules_end"} +close $wc diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/03f0:002a b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/03f0:002a index d77f4773fe..172fc5c7b1 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/03f0:002a +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/03f0:002a @@ -1,13 +1,7 @@ -####################################################### # HP LaserJet Professional P1102 -DefaultVendor= 0x03f0 -DefaultProduct=0x002a - TargetClass=0x07 MessageContent="555342431234567800000000000006d0000000000000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0408:ea25 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0408:ea25 new file mode 100644 index 0000000000..3b920a6da5 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0408:ea25 @@ -0,0 +1,6 @@ +# Quanta 1K3 + +TargetVendor= 0x0408 +TargetProduct= 0xea26 + +QuantaMode=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0408:f000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0408:f000 new file mode 100644 index 0000000000..0306c7a3ca --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0408:f000 @@ -0,0 +1,7 @@ +# Yota Router (Quanta 1QDLZZZ0ST2) + +TargetVendor= 0x0408 +TargetProduct= 0xd009 + +MessageContent="5553424312345678000000000000061b004600000000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:060c b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:060c index 59c682f8e2..e52468a8ce 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:060c +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:060c @@ -1,12 +1,7 @@ -######################################################## # Nokia CS-10 -DefaultVendor= 0x0421 -DefaultProduct=0x060c - TargetVendor= 0x0421 TargetProduct= 0x060e -CheckSuccess=20 - MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0610 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0610 index 408ee2d0ee..7ba00774c7 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0610 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0610 @@ -1,12 +1,7 @@ -######################################################## # Nokia CS-15 -DefaultVendor= 0x0421 -DefaultProduct=0x0610 - TargetVendor= 0x0421 TargetProduct= 0x0612 -CheckSuccess=20 - MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0618 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0618 new file mode 100644 index 0000000000..01627b9cfc --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0618 @@ -0,0 +1,7 @@ +# Nokia CS-12 + +TargetVendor= 0x0421 +TargetProduct= 0x0619 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:061d b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:061d new file mode 100644 index 0000000000..4fcd8a3395 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:061d @@ -0,0 +1,7 @@ +# Nokia CS-11 + +TargetVendor= 0x0421 +TargetProduct= 0x061e + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0622 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0622 index b3f743e3ad..976f06ebfd 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0622 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0622 @@ -1,13 +1,7 @@ -####################################################### # Nokia CS-17 -DefaultVendor= 0x0421 -DefaultProduct=0x0622 - TargetVendor= 0x0421 TargetProduct= 0x0623 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0627 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0627 index 15df16cf7f..ef18406235 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0627 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0627 @@ -1,13 +1,7 @@ -####################################################### # Nokia CS-18 -DefaultVendor= 0x0421 -DefaultProduct=0x0627 - TargetVendor= 0x0421 -TargetProduct= 0x0612 +TargetProductList="0612,0629" MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:062c b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:062c new file mode 100644 index 0000000000..65dc6571bb --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:062c @@ -0,0 +1,7 @@ +# Nokia CS-19 + +TargetVendor= 0x0421 +TargetProduct= 0x062d + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0632 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0632 new file mode 100644 index 0000000000..f60c34cc41 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0632 @@ -0,0 +1,7 @@ +# Nokia CS-7M-01 + +TargetVendor= 0x0421 +TargetProduct= 0x0632 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0637 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0637 new file mode 100644 index 0000000000..a066be1d20 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0421:0637 @@ -0,0 +1,7 @@ +# Nokia CS-21M-02 + +TargetVendor= 0x0421 +TargetProduct= 0x0638 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0471:1210:uMa=Philips b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0471:1210:uMa=Philips index 8d77b51159..d1e3a73f9d 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0471:1210:uMa=Philips +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0471:1210:uMa=Philips @@ -1,13 +1,7 @@ -######################################### # Philips (?) -DefaultVendor= 0x0471 -DefaultProduct=0x1210 - TargetClass=0xff MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -# Device will not "vanish", skip check -#CheckSuccess=20 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0471:1210:uMa=Wisue b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0471:1210:uMa=Wisue index 10d17ba9d2..11009e2757 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0471:1210:uMa=Wisue +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0471:1210:uMa=Wisue @@ -1,14 +1,8 @@ -######################################### # Vodafone MD950 (Wisue Technology) -DefaultVendor= 0x0471 -DefaultProduct=0x1210 - TargetVendor= 0x1dbc TargetProduct= 0x0005 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0471:1237 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0471:1237 index b81a94122a..6fccfcfeda 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0471:1237 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0471:1237 @@ -1,14 +1,8 @@ -######################################################## # Philips TalkTalk (NXP Semiconductors "Dragonfly") -DefaultVendor= 0x0471 -DefaultProduct=0x1237 - TargetVendor= 0x0471 TargetProductList="1206,1234" MessageContent="5553424312345678000000000000061b000000030000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0482:024d b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0482:024d index a1adc1e796..609dda5806 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0482:024d +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0482:024d @@ -1,11 +1,6 @@ -######################################################## # Kyocera W06K CDMA modem -DefaultVendor= 0x0482 -DefaultProduct=0x024d - TargetClass=0x02 Configuration=2 -CheckSuccess=20 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04bb:bccd b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04bb:bccd new file mode 100644 index 0000000000..7f67075d3a --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04bb:bccd @@ -0,0 +1,7 @@ +# I-O Data WMX2-U Wimax + +TargetVendor= 0x04bb +TargetProduct= 0x0949 + +MessageContent="55534243f0298d8124000000800006bc626563240000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04cc:225c b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04cc:225c new file mode 100644 index 0000000000..7de0f550fd --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04cc:225c @@ -0,0 +1,6 @@ +# Alcatel-Lucent T930S + +TargetClass=0xff + +Configuration=2 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:680c b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:680c new file mode 100644 index 0000000000..6de4548036 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:680c @@ -0,0 +1,7 @@ +# Samsung GT-B1110 + +TargetVendor= 0x04e8 +TargetProduct= 0x6792 + +MessageContent="0902200001010080fa0904000002080650000705010200020007058102000200" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:689a b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:689a index 94231adddf..7212d765ad 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:689a +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:689a @@ -1,15 +1,9 @@ -####################################################### # Samsung GT-B3730 -DefaultVendor= 0x04e8 -DefaultProduct=0x689a - TargetVendor= 0x04e8 TargetProduct= 0x6889 MessageContent="55534243785634120100000080000601000000000000000000000000000000" -CheckSuccess=20 - #NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:f000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:f000 index c06f04698e..78784995c5 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:f000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:f000 @@ -1,9 +1,6 @@ ######################################################## # Samsung U209 -DefaultVendor= 0x04e8 -DefaultProduct=0xf000 - TargetVendor= 0x04e8 TargetProduct= 0x6601 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:f000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:f000:sMo=U209 similarity index 54% copy from release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:f000 copy to release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:f000:sMo=U209 index c06f04698e..d1868adbe3 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:f000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/04e8:f000:sMo=U209 @@ -1,12 +1,7 @@ -######################################################## # Samsung U209 -DefaultVendor= 0x04e8 -DefaultProduct=0xf000 - TargetVendor= 0x04e8 TargetProduct= 0x6601 -CheckSuccess=20 - MessageContent="55534243123456780000000000000616000000000000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/057c:84ff b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/057c:84ff index e288433cca..597fcb644d 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/057c:84ff +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/057c:84ff @@ -1,14 +1,8 @@ -######################################################## # AVM Fritz!Wlan USB Stick N -DefaultVendor= 0x057c -DefaultProduct=0x84ff - TargetVendor= 0x057c TargetProduct= 0x8401 -CheckSuccess=20 - MessageContent="5553424312345678000000000000061b000000ff0000000000000000000000" NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:0010 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:0010 new file mode 100644 index 0000000000..58d55635d1 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:0010 @@ -0,0 +1,7 @@ +# Axesstel Modems (w/ initial idProduct 0x0010) + +TargetVendor= 0x05c6 +TargetProduct= 0x00a0 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:sVe=GT b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:sVe=GT index 0bafdd64a9..5f159efda8 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:sVe=GT +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:sVe=GT @@ -1,13 +1,8 @@ -####################################################### # Option GlobeTrotter GX0201 -DefaultVendor= 0x05c6 -DefaultProduct=0x1000 - TargetVendor= 0x0af0 TargetProduct= 0x6701 -CheckSuccess=20 - MessageContent="55534243123456780000000000000601000000000000000000000000000000" NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:sVe=Option b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:sVe=Option index f8d776772c..3ab865e2eb 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:sVe=Option +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:sVe=Option @@ -1,14 +1,9 @@ -####################################################### # Older Option devices -DefaultVendor= 0x05c6 -DefaultProduct= 0x1000 - TargetVendor= 0x0af0 TargetProductList="6901,6701,6600" -CheckSuccess=20 - MessageContent="55534243123456780000000000000601000000000000000000000000000000" NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=AnyDATA b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=AnyDATA index c46d7bc774..673138e1e9 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=AnyDATA +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=AnyDATA @@ -1,14 +1,7 @@ -######################################################## # AnyDATA devices, Bless UC165 -DefaultVendor= 0x05c6 -DefaultProduct= 0x1000 - TargetVendor= 0x16d5 TargetProduct= 0x6502 -CheckSuccess=20 - MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=CELOT b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=CELOT new file mode 100644 index 0000000000..4ed2a8095c --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=CELOT @@ -0,0 +1,7 @@ +# Celot K-300 + +TargetVendor= 0x211f +TargetProduct= 0x6801 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=DGT b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=DGT new file mode 100644 index 0000000000..b2d066e311 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=DGT @@ -0,0 +1,7 @@ +# Celot CT-680 + +TargetVendor= 0x211f +TargetProduct= 0x6802 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=Option b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=Option index 3965ed7317..a3f2a1b342 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=Option +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=Option @@ -1,13 +1,8 @@ -####################################################### # Quirky Option devices -DefaultVendor= 0x05c6 -DefaultProduct=0x1000 - TargetVendor= 0x0af0 TargetProduct= 0x6901 -CheckSuccess=10 - MessageContent="55534243123456780000000000000601000000000000000000000000000000" NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=SAMSUNG b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=SAMSUNG index bbdda60182..a433f31295 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=SAMSUNG +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=SAMSUNG @@ -1,14 +1,7 @@ -######################################################## # Samsung SGH-Z810 -DefaultVendor= 0x05c6 -DefaultProduct= 0x1000 - TargetVendor= 0x04e8 TargetProduct= 0x6601 -CheckSuccess=20 - MessageContent="55534243123456780000000000000616000000000000000000000000000000" - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=SSE b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=SSE index 322a4fa3c2..88d40e534c 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=SSE +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=SSE @@ -1,14 +1,8 @@ -######################################################## # Prolink P2000 CDMA -DefaultVendor= 0x05c6 -DefaultProduct=0x1000 - TargetVendor= 0x05c6 TargetProduct= 0x6000 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=StrongRising b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=StrongRising new file mode 100644 index 0000000000..296cb06e3c --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=StrongRising @@ -0,0 +1,7 @@ +# StrongRising device + +TargetVendor= 0x028a +TargetProduct= 0x1006 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=Vertex b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=Vertex index 1562e5e54d..429e748493 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=Vertex +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:1000:uMa=Vertex @@ -1,12 +1,7 @@ -######################################################## # Vertex Wireless 100 Series -DefaultVendor= 0x05c6 -DefaultProduct=0x1000 - TargetVendor= 0x1fe7 TargetProduct= 0x0100 -CheckSuccess=20 - MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:2000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:2000 index a3b2f983a8..85fbb121e4 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:2000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:2000 @@ -1,9 +1,5 @@ -######################################################## # HSDPA USB modem from dealextreme -DefaultVendor= 0x05c6 -DefaultProduct=0x2000 - TargetVendor= 0x05c6 TargetProduct= 0x0015 @@ -12,3 +8,4 @@ MessageContent="5553424368032c882400000080000612000000240000000000000000000000" NeedResponse=1 CheckSuccess=40 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:2001 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:2001 index 8e660cebd6..3d3652eaa2 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:2001 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:2001 @@ -1,14 +1,8 @@ -######################################################## # D-Link DWM-162-U5, Micromax MMX 300c -DefaultVendor= 0x05c6 -DefaultProduct=0x2001 - TargetVendor= 0x1e0e TargetProductList="ce16,cefe" MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:6503 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:6503 new file mode 100644 index 0000000000..ff29fa4ee7 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:6503 @@ -0,0 +1,7 @@ +# AnyDATA APE-540H + +TargetVendor= 0x16d5 +TargetProduct= 0x6502 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:f000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:f000 index 1cb294bfd9..233aa9e022 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:f000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c6:f000 @@ -1,12 +1,7 @@ -######################################################## # Siptune LM-75 ("LinuxModem") -DefaultVendor= 0x05c6 -DefaultProduct=0xf000 - TargetVendor= 0x05c6 TargetProduct= 0x9000 -CheckSuccess=20 - MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c7:1000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c7:1000 index 1981879dfa..223f4e1cb0 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c7:1000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/05c7:1000 @@ -1,13 +1,7 @@ -####################################################### # Qtronix EVDO 3G Modem (for TianYi) -DefaultVendor= 0x05c7 -DefaultProduct=0x1000 - TargetVendor= 0x05c7 TargetProduct= 0x6000 MessageContent="5553424312345678c00000008000069f140000000000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/072f:100d b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/072f:100d index 2e493973e1..793d5c7c57 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/072f:100d +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/072f:100d @@ -1,16 +1,11 @@ -######################################################## # InfoCert Business Key (SmartCard/Reader emulation) -DefaultVendor= 0x072f -DefaultProduct=0x100d - TargetVendor= 0x072f TargetProduct= 0x90cc MessageEndpoint=0x02 -CheckSuccess=20 - MessageContent="01b0000000000000000000000000000000000000000000000000000000000000" NoDriverLoading=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/07d1:a800 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/07d1:a800 index 04a27dce2b..a4d2585373 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/07d1:a800 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/07d1:a800 @@ -1,13 +1,7 @@ -######################################################## # D-Link DWM-156 HSUPA 3.75G USB Modem -DefaultVendor= 0x07d1 -DefaultProduct=0xa800 - TargetVendor= 0x07d1 TargetProduct= 0x3e02 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/07d1:a804 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/07d1:a804 index 17c7d9e314..e483ee1014 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/07d1:a804 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/07d1:a804 @@ -1,13 +1,7 @@ -######################################################## # D-Link DWM-156 HSUPA 3.75G USB Modem -DefaultVendor= 0x07d1 -DefaultProduct=0xa804 - TargetVendor= 0x07d1 TargetProduct= 0x7e11 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0922:1001 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0922:1001 new file mode 100644 index 0000000000..31d79051f7 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0922:1001 @@ -0,0 +1,11 @@ +# Dymo LabelManager + +TargetVendor= 0x0922 +TargetProduct= 0x1002 + +MessageEndpoint= 0x01 +ResponseEndpoint=0x01 + +MessageContent="1b5a01" +NoDriverLoading=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0930:0d46 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0930:0d46 index 32e1688f7e..2bbc5a54fa 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0930:0d46 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0930:0d46 @@ -1,14 +1,8 @@ -######################################################## # Toshiba G450 -DefaultVendor= 0x0930 -DefaultProduct=0x0d46 - TargetVendor= 0x0930 TargetProduct= 0x0d45 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0ace:2011 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0ace:2011 index 3cdfcf7108..944306292c 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0ace:2011 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0ace:2011 @@ -1,11 +1,5 @@ -######################################################## # Zydas ZD1211RW WLAN USB, Sphairon HomeLink 1202 (Variant 1) -DefaultVendor= 0x0ace -DefaultProduct=0x2011 - -CheckSuccess=20 - MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0ace:20ff b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0ace:20ff index 615342168d..ab94d119a6 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0ace:20ff +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0ace:20ff @@ -1,11 +1,5 @@ -######################################################## # Zydas ZD1211RW WLAN USB, Sphairon HomeLink 1202 (Variant 2) -DefaultVendor= 0x0ace -DefaultProduct=0x20ff - -CheckSuccess=20 - MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:4007 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:4007 new file mode 100644 index 0000000000..7350ba8d39 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:4007 @@ -0,0 +1,8 @@ +# Option iCon 711 + +TargetVendor= 0x0af0 +TargetProduct= 0x4005 + +SierraMode=1 + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6711 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6711 index 0e915efe3d..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6711 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6711 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x6711 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6731 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6731 index 252b4a1cf4..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6731 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6731 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x6731 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6751 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6751 index 048094adfd..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6751 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6751 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x6751 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6771 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6771 index 442e926a82..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6771 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6771 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x6771 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6791 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6791 index 298f53d9d5..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6791 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6791 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x6791 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6811 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6811 index a0e49bafb6..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6811 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6811 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x6811 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6911 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6911 index d00f8227c9..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6911 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6911 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x6911 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6951 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6951 index 90f1d32601..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6951 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6951 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x6951 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6971 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6971 index 856416a61e..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6971 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:6971 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x6971 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7011 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7011 index f6492f9d81..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7011 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7011 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7011 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7031 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7031 index 0641d3a937..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7031 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7031 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7031 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7051 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7051 index 0df8a332c1..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7051 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7051 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7051 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7071 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7071 index 0fe4bcf46e..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7071 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7071 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7071 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7111 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7111 index a32c7b5c1e..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7111 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7111 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7111 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7211 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7211 index ef5ceed70e..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7211 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7211 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7211 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7251 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7251 index 2e9072bc3c..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7251 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7251 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7251 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7271 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7271 index a008daeb9f..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7271 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7271 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7271 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7301 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7301 index 40339dec5f..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7301 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7301 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7301 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7311 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7311 index f2c1bd8e80..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7311 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7311 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7311 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7361 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7361 index e5b38229cd..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7361 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7361 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7361 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7381 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7381 index cd286c72df..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7381 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7381 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7381 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7401 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7401 index 9e6f1926bc..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7401 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7401 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7401 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7501 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7501 index dd48d61a6c..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7501 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7501 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7501 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7601 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7601 index 4cb246fdc6..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7601 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7601 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7601 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7701 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7701 index 3caef81a00..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7701 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7701 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7701 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7706 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7706 new file mode 100644 index 0000000000..9ce00fabdf --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7706 @@ -0,0 +1,7 @@ +# Option HSO device + +TargetClass=0xff + +MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7801 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7801 index ebd3fd9dec..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7801 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7801 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7801 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7901 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7901 index 1634919be7..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7901 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7901 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x7901 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7a01 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7a01 index e48de11726..231bbfb65d 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7a01 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7a01 @@ -1,12 +1,7 @@ -######################################################## # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct=0x7a01 - TargetClass=0xff MessageContent="55534243123456780000000000000601000000000000000000000000000000" -CheckSuccess=20 - +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7a05 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7a05 index e27b54f87c..26818c7e01 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7a05 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:7a05 @@ -1,12 +1,7 @@ -######################################################## # Option iCon 461 -DefaultVendor= 0x0af0 -DefaultProduct=0x7a05 - TargetClass=0xff MessageContent="55534243123456780000000000000601000000000000000000000000000000" -CheckSuccess=20 - +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8006 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8006 new file mode 100644 index 0000000000..e182b5c0b2 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8006 @@ -0,0 +1,8 @@ +# Option Globetrotter (Variant) + +TargetVendor= 0x0af0 +TargetProduct= 0x9100 + +MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8200 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8200 index aa42ff4ab8..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8200 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8200 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x8200 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8201 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8201 index bbc33f19de..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8201 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8201 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x8201 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8300 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8300 index b5819e7109..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8300 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8300 @@ -1,11 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x8300 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8302 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8302 index 9573bd4e7c..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8302 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8302 @@ -1,11 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x8302 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8304 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8304 index ea3de77965..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8304 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8304 @@ -1,11 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x8304 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8400 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8400 index eab8662d66..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8400 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8400 @@ -1,11 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x8400 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8600 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8600 index 662e399dd3..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8600 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8600 @@ -1,11 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x8600 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8700 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8700 new file mode 100644 index 0000000000..47e7fb33d6 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8700 @@ -0,0 +1,7 @@ +# Option GI0643 (aka XYFI) + +TargetClass=0xff + +MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8800 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8800 index cd1d856531..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8800 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8800 @@ -1,11 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x8800 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8900 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8900 index 225bbbcf82..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8900 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:8900 @@ -1,11 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x8900 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:9000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:9000 index 651c704a6f..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:9000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:9000 @@ -1,11 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0x9000 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:9200 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:9200 new file mode 100644 index 0000000000..9ce00fabdf --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:9200 @@ -0,0 +1,7 @@ +# Option HSO device + +TargetClass=0xff + +MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:c031 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:c031 index fc6172fd7c..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:c031 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:c031 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xc031 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:c100 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:c100 index 8854f30994..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:c100 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:c100 @@ -1,11 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xc100 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d001 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d001 new file mode 100644 index 0000000000..53af83c3ca --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d001 @@ -0,0 +1,8 @@ +# Option GlobeTrotter GI1515 + +TargetVendor= 0x0af0 +TargetProduct= 0xd157 + +MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d013 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d013 index 0da431bf60..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d013 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d013 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xd013 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d031 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d031 index 2fb7c1093e..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d031 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d031 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xd031 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d033 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d033 index 8eef8f1641..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d033 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d033 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xd033 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d035 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d035 index 5d7b9ac376..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d035 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d035 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xd035 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d055 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d055 index 4a34534a06..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d055 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d055 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xd055 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d057 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d057 index e6a3155bb1..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d057 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d057 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xd057 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d058 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d058 index 4f5bb108bc..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d058 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d058 @@ -1,11 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xd058 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d155 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d155 index 02358fe811..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d155 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d155 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xd155 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d157 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d157 index 59c8fb9f98..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d157 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d157 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xd157 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d255 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d255 index f67dff4580..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d255 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d255 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xd255 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d257 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d257 index 3ac8eb6ca2..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d257 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d257 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xd257 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d357 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d357 index ca3e9c15b7..9ce00fabdf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d357 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0af0:d357 @@ -1,14 +1,7 @@ -####################################################### # Option HSO device -DefaultVendor= 0x0af0 -DefaultProduct= 0xd357 - -TargetClass= 0xff - -CheckSuccess=10 +TargetClass=0xff MessageContent="55534243785634120100000080000601000000000000000000000000000000" NoDriverLoading=1 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0b3c:c700 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0b3c:c700 index 4db1d490d1..baa3494661 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0b3c:c700 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0b3c:c700 @@ -1,13 +1,8 @@ -######################################################## # Olivetti Olicard 100 and others -DefaultVendor= 0x0b3c -DefaultProduct=0xc700 - TargetVendor= 0x0b3c TargetProductList="c000,c001,c002" MessageContent="5553424312345678000000000000061b000000030000000000000000000000" NeedResponse=1 -CheckSuccess=20 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0b3c:f000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0b3c:f000 index ba8074060f..f4734f104e 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0b3c:f000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0b3c:f000 @@ -1,13 +1,8 @@ -######################################################## # Olivetti Olicard 145, 155 -DefaultVendor= 0x0b3c -DefaultProduct=0xf000 - TargetVendor= 0x0b3c TargetProductList="c003,c004" MessageContent="5553424312345678c000000080010606f50402527000000000000000000000" NeedResponse=1 -CheckSuccess=20 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0cf3:20ff b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0cf3:20ff index 34b0e1614b..dd6e5dd229 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0cf3:20ff +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0cf3:20ff @@ -1,14 +1,9 @@ -######################################################## # Atheros Wireless / Netgear WNDA3200 -DefaultVendor= 0x0cf3 -DefaultProduct=0x20ff - TargetVendor= 0x0cf3 TargetProduct= 0x7010 -CheckSuccess=10 -NoDriverLoading=1 - MessageContent="5553424329000000000000000000061b000000020000000000000000000000" NeedResponse=1 + +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0d46:45a1 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0d46:45a1 index 08e9db9467..5b40c4febf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0d46:45a1 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0d46:45a1 @@ -1,13 +1,7 @@ -####################################################### # Kobil mIdentity 3G (1) -DefaultVendor= 0x0d46 -DefaultProduct=0x45a1 - TargetVendor= 0x0d46 TargetProduct= 0x45a9 KobilMode=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0d46:45a5 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0d46:45a5 index 1181a088c7..ac01d78ae6 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0d46:45a5 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0d46:45a5 @@ -1,13 +1,7 @@ -####################################################### # Kobil mIdentity 3G (2) -DefaultVendor= 0x0d46 -DefaultProduct=0x45a5 - TargetVendor= 0x0d46 TargetProduct= 0x45ad KobilMode=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0df7:0800 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0df7:0800 index 4f5495732b..14ea7a91d2 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0df7:0800 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0df7:0800 @@ -1,13 +1,7 @@ -####################################################### # Mobile Action ("Smart Cable") -DefaultVendor= 0x0df7 -DefaultProduct=0x0800 - TargetClass=0xff MobileActionMode=1 NoDriverLoading=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0e8d:0002 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0e8d:0002 new file mode 100644 index 0000000000..a69391b9dc --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0e8d:0002 @@ -0,0 +1,7 @@ +# MediaTek MT6276M + +TargetVendor= 0x0e8d +TargetProductList="00a1,00a2" + +MessageContent="555342431234567800000000000006f0010300000000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0e8d:0002:uPr=MT b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0e8d:0002:uPr=MT new file mode 100644 index 0000000000..a69391b9dc --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0e8d:0002:uPr=MT @@ -0,0 +1,7 @@ +# MediaTek MT6276M + +TargetVendor= 0x0e8d +TargetProductList="00a1,00a2" + +MessageContent="555342431234567800000000000006f0010300000000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0e8d:7109 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0e8d:7109 index 200c7bdbb4..6a939929b8 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0e8d:7109 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0e8d:7109 @@ -1,9 +1,5 @@ -######################################################## # MediaTek Wimax USB Card -DefaultVendor= 0x0e8d -DefaultProduct=0x7109 - TargetVendor= 0x0e8d TargetProduct= 0x7118 @@ -11,5 +7,3 @@ MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" NoDriverLoading=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fce:d0cf b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fce:d0cf index a5f3bdb5a4..6de197bae8 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fce:d0cf +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fce:d0cf @@ -1,12 +1,7 @@ -######################################################## # Sony Ericsson MD300 -DefaultVendor= 0x0fce -DefaultProduct=0xd0cf - TargetClass=0x02 -CheckSuccess=20 - DetachStorageOnly=1 Configuration=3 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fce:d0e1 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fce:d0e1 index d9e08d0b08..9ed8af66f6 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fce:d0e1 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fce:d0e1 @@ -1,12 +1,7 @@ -######################################################## # Sony Ericsson MD400 -DefaultVendor= 0x0fce -DefaultProduct=0xd0e1 - TargetClass=0x02 -CheckSuccess=20 - SonyMode=1 Configuration=2 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fce:d103 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fce:d103 index 185a97aff1..f2b51669ad 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fce:d103 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fce:d103 @@ -1,13 +1,7 @@ -######################################################## # Sony Ericsson MD400G -DefaultVendor= 0x0fce -DefaultProduct=0xd103 - TargetClass=0x02 SonyMode=1 Configuration=2 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fd1:1000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fd1:1000 index 260ab1cb12..5ec62f6819 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fd1:1000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/0fd1:1000 @@ -1,12 +1,6 @@ -####################################################### # GW D301 (Advinne AMC) -DefaultVendor= 0x0fd1 -DefaultProduct=0x1000 - TargetClass=0xff Configuration=3 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:1000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:1000 index 9614cafe85..62001b6d33 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:1000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:1000 @@ -1,11 +1,6 @@ -######################################################## # LG LDU-1900D EV-DO (Rev. A) -DefaultVendor= 0x1004 -DefaultProduct=0x1000 - TargetClass=0xff -CheckSuccess=20 - MessageContent="55534243123456780000000000000aff554d53434847000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:607f b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:607f index a45c65274f..2239f0d3f5 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:607f +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:607f @@ -1,13 +1,8 @@ -######################################################## # LG HDM-2100 (EVDO Rev.A USB modem) -DefaultVendor= 0x1004 -DefaultProduct=0x607f - TargetVendor= 0x1004 TargetProductList="6000,6114" -MessageContent="1201100102000040041014610000010200018006000100001200" - -CheckSuccess=20 +MessageContent="555342431234567803000000800006f1010100000000000000000000000000" +NeedResponse=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:613a b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:613a index 37cb0dab79..2f5236dd22 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:613a +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:613a @@ -1,13 +1,8 @@ -######################################################## # LG L-05A -DefaultVendor= 0x1004 -DefaultProduct=0x613a - TargetVendor= 0x1004 TargetProduct= 0x6124 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:613f b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:613f index 825855706b..31ae811236 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:613f +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:613f @@ -1,13 +1,8 @@ -######################################################## # LG LUU-2100TI (aka AT&T USBConnect Turbo) -DefaultVendor= 0x1004 -DefaultProduct=0x613f - TargetVendor= 0x1004 TargetProduct= 0x6141 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:614e similarity index 52% copy from release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 copy to release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:614e index d68e17a7e3..09846bd826 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:614e @@ -1,13 +1,9 @@ -######################################################## -# ZTE MU351 +# LG L-07A -DefaultVendor= 0x19d2 -DefaultProduct=0x0003 - -TargetClass=0xff +TargetVendor= 0x1004 +TargetProduct= 0x6135 MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -CheckSuccess=20 +NeedResponse=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:6156 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:6156 new file mode 100644 index 0000000000..a47469f065 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:6156 @@ -0,0 +1,7 @@ +# LG LUU-2110TI + +TargetVendor= 0x1004 +TargetProduct= 0x6157 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" +NeedResponse=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:6190 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:6190 index 6b9c3456d1..5efb8a2ddd 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:6190 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:6190 @@ -1,13 +1,7 @@ -####################################################### # LG AD600 -DefaultVendor= 0x1004 -DefaultProduct=0x6190 - TargetVendor= 0x1004 TargetProduct= 0x61a7 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61aa b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61aa new file mode 100644 index 0000000000..ff9179698b --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61aa @@ -0,0 +1,6 @@ +# LG VL600 + +TargetVendor= 0x1004 +TargetProduct= 0x61a7 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" \ No newline at end of file diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61dd b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61dd new file mode 100644 index 0000000000..12318e0bb9 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61dd @@ -0,0 +1,8 @@ +# LG L-02C LTE + +TargetVendor= 0x1004 +TargetProduct= 0x618f + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" +NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61e7 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61e7 new file mode 100644 index 0000000000..13245c74a5 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61e7 @@ -0,0 +1,7 @@ +# LG SD711 + +TargetVendor= 0x1004 +TargetProduct= 0x61e6 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61eb b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61eb new file mode 100644 index 0000000000..52177136ef --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:61eb @@ -0,0 +1,8 @@ +# LG L-08C (NTT docomo) + +TargetVendor= 0x1004 +TargetProduct= 0x61ea + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" +NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:6327 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:6327 new file mode 100644 index 0000000000..41db7f6622 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1004:6327 @@ -0,0 +1,7 @@ +# LG L-03D LTE/3G + +TargetVendor= 0x1004 +TargetProduct= 0x6326 + +MessageContent=555342431234567800000000000005f1010100000000000000000000000000 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1033:0035 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1033:0035 index 9c509844b5..263b5319c2 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1033:0035 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1033:0035 @@ -1,12 +1,7 @@ -######################################################## # Huawei E630 -DefaultVendor= 0x1033 -DefaultProduct=0x0035 - TargetVendor= 0x12d1 TargetProduct= 0x1003 -CheckSuccess=20 - HuaweiMode=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b03 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b03 index 81ee553e7c..6e0095baac 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b03 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b03 @@ -1,12 +1,7 @@ -######################################################## # UTStarcom UM175 (distributor "Alltel") -DefaultVendor= 0x106c -DefaultProduct=0x3b03 - TargetVendor= 0x106c TargetProduct= 0x3715 -CheckSuccess=20 - MessageContent="555342431234567824000000800008ff024445564348470000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b05 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b05 index e10e6ae4d8..ff9b5569d0 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b05 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b05 @@ -1,13 +1,7 @@ -####################################################### # Pantech / UTStarcom UMW190 (Verizon) -DefaultVendor= 0x106c -DefaultProduct=0x3b05 - TargetVendor= 0x106c TargetProduct= 0x3716 MessageContent="555342431234567824000000800008ff020000000000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b06 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b06 index cc4935e808..98b524082a 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b06 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b06 @@ -1,12 +1,7 @@ -######################################################## # UTStarcom UM185E (distributor "Alltel") -DefaultVendor= 0x106c -DefaultProduct=0x3b06 - TargetVendor= 0x106c TargetProduct= 0x3717 -CheckSuccess=20 - MessageContent="55534243b82e238c24000000800008ff020000000000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b11 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b11 new file mode 100644 index 0000000000..bcb827bf9a --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b11 @@ -0,0 +1,7 @@ +# Pantech UML290 + +TargetVendor= 0x106c +TargetProduct= 0x3718 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b14 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b14 new file mode 100644 index 0000000000..c82946caef --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/106c:3b14 @@ -0,0 +1,8 @@ +# Option Beemo / Pantech P4200 LTE + +TargetVendor= 0x106c +TargetProduct= 0x3721 + +MessageContent="555342431234567824000000800008ff024445564348470000000000000000" +NoDriverLoading=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1076:7f40 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1076:7f40 index aed11d367c..2f5b9fe6cc 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1076:7f40 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1076:7f40 @@ -1,12 +1,7 @@ -######################################################## # Sagem F@ST 9520-35-GLR -DefaultVendor= 0x1076 -DefaultProduct=0x7f40 - TargetVendor= 0x1076 TargetProduct= 0x7f00 -CheckSuccess=20 - GCTMode=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/109b:f009 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/109b:f009 new file mode 100644 index 0000000000..8846c6e6a6 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/109b:f009 @@ -0,0 +1,8 @@ +# Hisense E910 EVDO Phone + +TargetVendor= 0x109b +TargetProduct= 0x9114 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" +NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1199:0fff b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1199:0fff dissimilarity index 66% index 08f987c668..3039e3a32f 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1199:0fff +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1199:0fff @@ -1,14 +1,9 @@ -####################################################### -# Sierra devices (specific driver) - -DefaultVendor= 0x1199 -DefaultProduct= 0x0fff - -TargetVendor= 0x1199 -TargetProductList="0017,0018,0019,0020,0021,0022,0024,0026,0027,0028,0029,0112,0120,0218,0220,0224,6802,6803,6804,6805,6808,6809,6812,6813,6815,6816,6820,6821,6822,6832,6833,6834,6835,6838,6839,683a,683b,683c,683d,683e,6850,6851,6852,6853,6855,6856,6859,685a" - -SierraMode=1 - -CheckSuccess=10 - -NoDriverLoading=1 +# Sierra devices (specific driver) + +TargetVendor= 0x1199 +TargetProductList="0017,0018,0019,0020,0021,0022,0024,0026,0027,0028,0029,0112,0120,0218,0220,0224,0301,6802,6803,6804,6805,6808,6809,6812,6813,6815,6816,6820,6821,6822,6832,6833,6834,6835,6838,6839,683a,683b,683c,683d,683e,6850,6851,6852,6853,6855,6856,6859,685a,68a3" + +SierraMode=1 + +NoDriverLoading=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1266:1000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1266:1000 index a2d9e94c5a..45fef6b9f0 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1266:1000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1266:1000 @@ -1,9 +1,5 @@ -######################################################## # Digicom 8E4455 (and all Pirelli devices - EXPERIMENTAL) -DefaultVendor= 0x1266 -DefaultProduct=0x1000 - TargetVendor= 0x1266 TargetProductList="1002,1003,1004,1005,1006,1007,1008,1009,100a,100b,100c,100d,100e,100f,1011,1012" @@ -12,5 +8,3 @@ NeedResponse=1 MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424387654321000000000000061b000000020000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1001 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1001 index fe629b89e9..f32ac30e96 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1001 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1001 @@ -1,11 +1,6 @@ -######################################################## # Huawei E169 -DefaultVendor= 0x12d1 -DefaultProduct=0x1001 - TargetClass=0xff -CheckSuccess=20 - HuaweiMode=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1003 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1003 index a0c17be716..3b5fcc912e 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1003 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1003 @@ -1,11 +1,6 @@ -######################################################## # Huawei E220, E230, E270, E870 -DefaultVendor= 0x12d1 -DefaultProduct=0x1003 - TargetClass=0xff -CheckSuccess=20 - HuaweiMode=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1009 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1009 index 0906fb9c82..3f01491e47 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1009 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1009 @@ -1,13 +1,6 @@ -######################################################## # Huawei V725 Phone (aka Vodafone 725) -DefaultVendor= 0x12d1 -DefaultProduct=0x1009 - TargetClass=0xff - HuaweiMode=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:101e b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:101e index 726c07094a..227fc15b30 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:101e +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:101e @@ -1,11 +1,6 @@ -######################################################## # Huawei U7510 / U7517 -DefaultVendor= 0x12d1 -DefaultProduct=0x101e - TargetClass=0xff -CheckSuccess=20 - MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1030 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1030 index efa83732a4..69bda7c5df 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1030 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1030 @@ -1,14 +1,8 @@ -######################################################## # Huawei U8220, T-Mobile Pulse (Android smartphone) -DefaultVendor= 0x12d1 -DefaultProduct=0x1030 - TargetVendor= 0x12d1 TargetProduct= 0x1034 MessageContent="55534243123456780600000080010a11060000000000000000000000000000" NoDriverLoading=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1031 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1031 index 82d9bd98b1..d06c26fabc 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1031 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1031 @@ -1,15 +1,10 @@ -######################################################## # Huawei U8110 / Joy, Vodafone 845 (Android smartphone) -DefaultVendor= 0x12d1 -DefaultProduct=0x1031 - TargetVendor= 0x12d1 TargetProduct= 0x1035 -CheckSuccess=20 - MessageContent="55534243123456780600000080010a11060000000000000000000000000000" # for Android SDK NoDriverLoading=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1414 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1414 index f4ff7f91d8..ad428a07e7 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1414 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1414 @@ -1,11 +1,6 @@ -######################################################## # Huawei E180 -DefaultVendor= 0x12d1 -DefaultProduct=0x1414 - TargetClass=0xff -CheckSuccess=20 - HuaweiMode=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1446 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1446 index 2d81e5cb3a..b1272cb997 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1446 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1446 @@ -1,12 +1,7 @@ -######################################################## # Huawei, newer modems -DefaultVendor= 0x12d1 -DefaultProduct=0x1446 - TargetVendor= 0x12d1 -TargetProductList="1001,1406,140b,140c,1412,141b,1433,14ac,1506" - -CheckSuccess=20 +TargetProductList="1001,1406,140b,140c,1412,141b,1432,1433,1436,14ac,1506,150c,1511" MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1449 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1449 index f4ca83b5c1..fb1d1b0efc 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1449 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1449 @@ -1,13 +1,7 @@ -######################################################## # Huawei E352 -DefaultVendor= 0x12d1 -DefaultProduct=0x1449 - TargetVendor= 0x12d1 TargetProduct= 0x1444 MessageContent="55534243123456780000000000000011062000000100000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14ad b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14ad index 488a861685..7b600c315b 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14ad +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14ad @@ -1,12 +1,7 @@ -######################################################## # Vodafone (Huawei) K3806 -DefaultVendor= 0x12d1 -DefaultProduct=0x14ad - TargetVendor= 0x12d1 TargetProduct= 0x14ae -CheckSuccess=20 - MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14b5 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14b5 index 6630191fe1..ea7b8c07f8 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14b5 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14b5 @@ -1,15 +1,7 @@ -######################################################## # Huawei E173 (Viettel 3G) -DefaultVendor= 0x12d1 -DefaultProduct=0x14b5 - TargetVendor= 0x12d1 -TargetProduct= 0x14a8 +TargetProductList="14a8,14aa" -DefaultVendor= 0x12d1 -DefaultProduct=0x14b5 MessageContent="55534243123456780000000000000011062000000100000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14b7 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14b7 index 41da6c4268..6c29e83023 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14b7 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14b7 @@ -1,12 +1,7 @@ -######################################################## # Vodafone (Huawei) K4511 -DefaultVendor= 0x12d1 -DefaultProduct=0x14b7 - TargetVendor= 0x12d1 TargetProduct= 0x14cc -CheckSuccess=20 - MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14ba b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14ba new file mode 100644 index 0000000000..040d99542a --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14ba @@ -0,0 +1,7 @@ +# Huawei E173u-2, E177 + +TargetVendor= 0x12d1 +TargetProduct= 0x14d2 + +MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c1 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c1 index f282069391..19dea40dcb 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c1 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c1 @@ -1,12 +1,7 @@ -######################################################## # Vodafone (Huawei) K4605 -DefaultVendor= 0x12d1 -DefaultProduct=0x14c1 - TargetVendor= 0x12d1 TargetProduct= 0x14c6 -CheckSuccess=20 - MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c3 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c3 new file mode 100644 index 0000000000..a4ff120480 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c3 @@ -0,0 +1,7 @@ +# K5005 Vodafone/Huawei + +TargetVendor= 0x12d1 +TargetProduct= 0x14c8 + +MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c4 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c4 index 2689d187e5..c56c4f6f97 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c4 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c4 @@ -1,13 +1,7 @@ -######################################################## # Vodafone/Huawei K3771 -DefaultVendor= 0x12d1 -DefaultProduct=0x14c4 - TargetVendor= 0x12d1 TargetProduct= 0x14ca MessageContent="55534243123456780000000000000011062000000100000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c5 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c5 index 458b2073b7..e7c4b86923 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c5 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14c5 @@ -1,12 +1,7 @@ -######################################################## # Vodafone (Huawei) K4510 -DefaultVendor= 0x12d1 -DefaultProduct=0x14c5 - TargetVendor= 0x12d1 TargetProduct= 0x14cb -CheckSuccess=20 - MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14d1 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14d1 index cdccb3dac8..a7a1ea4874 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14d1 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14d1 @@ -1,13 +1,7 @@ -######################################################## -# Vodafone/Huawei K3770 - -DefaultVendor= 0x12d1 -DefaultProduct=0x14d1 +# Vodafone / Huawei K3770 TargetVendor= 0x12d1 TargetProduct= 0x14c9 MessageContent="55534243123456780000000000000011062000000100000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14fe b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14fe index 2bf0935798..c6a8bb017c 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14fe +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:14fe @@ -1,13 +1,7 @@ -######################################################## # T-Mobile NL (Huawei E352) -DefaultVendor= 0x12d1 -DefaultProduct=0x14fe - TargetVendor= 0x12d1 TargetProduct= 0x1506 MessageContent="55534243123456780000000000000011062000000100000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1505 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1505 index 5d578648f6..48d3b13c49 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1505 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1505 @@ -1,13 +1,6 @@ -######################################################## -# Huawei EC156 - -DefaultVendor= 0x12d1 -DefaultProduct=0x1505 +# Huawei EC156, Huawei E372u-8 TargetVendor= 0x12d1 -TargetProduct= 0x140b +TargetProductList="140b,1506,150f,150a" MessageContent="55534243123456780000000000000011062000000100000000000000000000" - -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:151a b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:151a new file mode 100644 index 0000000000..cbcd2de45d --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:151a @@ -0,0 +1,7 @@ +# Huawei E392u-12, E3131 (Variant) + +TargetVendor= 0x12d1 +TargetProductList="151b,151d" + +MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1520 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1520 index 4666f0db1e..a0131d822e 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1520 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1520 @@ -1,12 +1,7 @@ -######################################################## # Huawei K3765 -DefaultVendor= 0x12d1 -DefaultProduct=0x1520 - TargetVendor= 0x12d1 TargetProduct= 0x1465 -CheckSuccess=20 - MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1521 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1521 index 5e312c94ae..c40387aa4e 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1521 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1521 @@ -1,12 +1,7 @@ -######################################################## # Huawei K4505 -DefaultVendor= 0x12d1 -DefaultProduct=0x1521 - TargetVendor= 0x12d1 TargetProduct= 0x1464 -CheckSuccess=20 - MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1523 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1523 index edaac929ff..efadeb47ba 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1523 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1523 @@ -1,12 +1,7 @@ -######################################################## # Huawei R201 -DefaultVendor= 0x12d1 -DefaultProduct=0x1523 - TargetVendor= 0x12d1 TargetProduct= 0x1491 -CheckSuccess=20 - MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1526 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1526 new file mode 100644 index 0000000000..ddf0421c8d --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1526 @@ -0,0 +1,7 @@ +# Vodafone / Huawei K3772 + +TargetVendor= 0x12d1 +TargetProduct= 0x14cf + +MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1553 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1553 index bc79bc148f..af5e014862 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1553 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1553 @@ -1,13 +1,7 @@ -####################################################### # Huawei E1553 -DefaultVendor= 0x12d1 -DefaultProduct=0x1553 - TargetVendor= 0x12d1 TargetProduct= 0x1001 MessageContent="55534243123456780000000000000011062000000100000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1557 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1557 index 75e48bffd0..a66abc24c5 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1557 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1557 @@ -1,12 +1,7 @@ -######################################################## # Huawei E173 -DefaultVendor= 0x12d1 -DefaultProduct=0x1557 - TargetVendor= 0x12d1 TargetProduct= 0x14a5 -CheckSuccess=20 - MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:155b b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:155b new file mode 100644 index 0000000000..ae310fc1ce --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:155b @@ -0,0 +1,7 @@ +# Huawei E171 + +TargetVendor= 0x12d1 +TargetProduct= 0x1506 + +MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1805 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1805 new file mode 100644 index 0000000000..5967155eb0 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1805 @@ -0,0 +1,6 @@ +# Huawei U2800 Phone + +TargetClass=0xff + +MessageContent="55534243123456780600000080000601000000000000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1c0b b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1c0b index 07a9527c05..1d74ba04f3 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1c0b +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1c0b @@ -1,13 +1,7 @@ -####################################################### # Huawei E173s -DefaultVendor= 0x12d1 -DefaultProduct=0x1c0b - TargetVendor= 0x12d1 -TargetProductList="1c05,1c08" +TargetProductList="1c05,1c07,1c08,1c10" MessageContent="55534243123456780000000000000011062000000100000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1c1b b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1c1b new file mode 100644 index 0000000000..75f9d098e2 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1c1b @@ -0,0 +1,8 @@ +# Huawei GP02 (E587 Variant) + +TargetVendor= 0x12d1 +TargetProduct= 0x1506 + +MessageContent="55534243123456780000000000000011062000000100000000000000000000" +NoDriverLoading=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1c24 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1c24 new file mode 100644 index 0000000000..4a85e8112d --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1c24 @@ -0,0 +1,7 @@ +# Huawei E173 (Moviestar) + +TargetVendor= 0x12d1 +TargetProduct= 0x1c12 + +MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1d50 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1d50 new file mode 100644 index 0000000000..808bb8464b --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1d50 @@ -0,0 +1,6 @@ +# Huawei ET302 + +TargetClass=0xff + +Configuration=2 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1da1 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1da1 index fcb2c5ceea..4f7e3c5780 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1da1 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1da1 @@ -1,14 +1,6 @@ -######################################################## -# Huawei ET8282 +# Huawei ET8282, Huawei ET127 -DefaultVendor= 0x12d1 -DefaultProduct=0x1da1 - -TargetClass=0xff - -DefaultVendor= 0x12d1 +TargetVendor= 0x12d1 TargetProduct= 0x1d09 -HuaweiMode=1 - -CheckSuccess=20 +HuaweiMode=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1f01 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1f01 index 8f40a133bd..07271f0dc9 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1f01 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1f01 @@ -1,11 +1,8 @@ -####################################################### -# Huawei E3131s-2 +# Huawei E353 (3.se) -DefaultVendor= 0x12d1 -DefaultProduct=0x1f01 - -TargetVendor= 0x12d1 +TargetVendor= 0x12d1 TargetProduct= 0x14db MessageContent="55534243123456780000000000000a11062000000000000100000000000000" NoDriverLoading=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1f03 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1f03 new file mode 100644 index 0000000000..c9b43d703e --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1f03 @@ -0,0 +1,8 @@ +# KDDI (Huawei) HWD12 LTE + +TargetVendor= 0x12d1 +TargetProduct= 0x14db + +MessageContent="55534243123456780000000000000a11062000000000000100000000000000" +NoDriverLoading=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1f11 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1f11 new file mode 100644 index 0000000000..7dc3bfc071 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:1f11 @@ -0,0 +1,7 @@ +# Vodafone / Huawei K3773 + +TargetVendor= 0x12d1 +TargetProduct= 0x14bc + +MessageContent="55534243123456780000000000000011062000000100000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:380b b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:380b index 741ed77283..47cf15723b 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:380b +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/12d1:380b @@ -1,14 +1,8 @@ -####################################################### # Huawei BM358 WiMAX -DefaultVendor= 0x12d1 -DefaultProduct=0x380b - TargetClass=0x02 MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1307:1169 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1307:1169 index 12241a7cc6..924f6c3633 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1307:1169 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1307:1169 @@ -1,14 +1,8 @@ -######################################################## # Cisco AM10 "Valet Connector" -DefaultVendor= 0x1307 -DefaultProduct=0x1169 - TargetVendor= 0x13b1 TargetProduct= 0x0031 CiscoMode=1 NoDriverLoading=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5010 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5010 index 5d99f8f4b2..adad4babb7 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5010 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5010 @@ -1,12 +1,7 @@ -######################################################## # Novatel Wireless devices -DefaultVendor= 0x1410 -DefaultProduct=0x5010 - TargetVendor= 0x1410 TargetProductList="4100,4400,7030" -CheckSuccess=20 - MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5020 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5020 index 2d9018c1ee..95e8fbc6f3 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5020 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5020 @@ -1,12 +1,7 @@ -######################################################## # Novatel MC990D -DefaultVendor= 0x1410 -DefaultProduct=0x5020 +TargetVendor=0x1410 +TargetProductList="6000,7001" -TargetClass=0xff - -CheckSuccess=20 - -Interface= 5 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5023 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5023 new file mode 100644 index 0000000000..1cbe757c74 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5023 @@ -0,0 +1,7 @@ +# Novatel MC996D + +TargetVendor= 0x1410 +TargetProduct=0x7030 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5030 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5030 index f484f4b1f3..03ef62d8e4 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5030 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5030 @@ -1,12 +1,7 @@ -######################################################## # Novatel U760 -DefaultVendor= 0x1410 -DefaultProduct=0x5030 - TargetVendor= 0x1410 TargetProduct= 0x6000 -CheckSuccess=20 - MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5031 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5031 index 006e180a4f..4e85bde776 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5031 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5031 @@ -1,12 +1,7 @@ -######################################################## # Novatel MC760 3G -DefaultVendor= 0x1410 -DefaultProduct=0x5031 - TargetVendor= 0x1410 TargetProduct= 0x6002 -CheckSuccess=20 - MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5041 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5041 index 14e5e1ae68..40b4c4935d 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5041 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5041 @@ -1,12 +1,7 @@ -######################################################## # Novatel Generic MiFi 2352 / Vodafone MiFi 2352 -DefaultVendor= 0x1410 -DefaultProduct=0x5041 - TargetVendor= 0x1410 TargetProductList="7001,7003" -CheckSuccess=20 - MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5059 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5059 new file mode 100644 index 0000000000..5dfda58253 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:5059 @@ -0,0 +1,8 @@ +# Novatel Wireless MC545 HSPA, U679 LTE + +TargetVendor= 0x1410 +TargetProductList="7031,7042" + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" +NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:7001 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:7001 new file mode 100644 index 0000000000..f3ca49f1a9 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1410:7001 @@ -0,0 +1,5 @@ +# Novatel Generic MiFi 2372 / Vodafone MiFi 2372 + +TargetClass=0xff + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/148e:a000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/148e:a000 index 0fee01a6bb..81e01a1a19 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/148e:a000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/148e:a000 @@ -1,11 +1,6 @@ -######################################################## # Sequans SQN1210/SQN1220 (generic chipsets) -DefaultVendor= 0x148e -DefaultProduct=0xa000 - TargetClass=0x02 SequansMode=1 -CheckSuccess=20 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/148f:2578 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/148f:2578 index fee87d4be3..aedb3d5271 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/148f:2578 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/148f:2578 @@ -1,14 +1,8 @@ -######################################################## # Motorola 802.11 bg WLAN (TER/GUSB3-E) -DefaultVendor= 0x148f -DefaultProduct=0x2578 - TargetVendor= 0x148f TargetProduct= 0x9021 -CheckSuccess=20 - MessageContent="55534243908ecd89000000000000061b000000020000000000000000000000" NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/15eb:7153 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/15eb:7153 new file mode 100644 index 0000000000..1239135dd7 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/15eb:7153 @@ -0,0 +1,7 @@ +# China TeleCom CBP7.0 + +TargetVendor= 0x15eb +TargetProduct= 0x7152 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:6281 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:6281 index 0c60a6e9f5..33b0063031 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:6281 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:6281 @@ -1,12 +1,6 @@ -######################################################## # C-motech CHU-628S -DefaultVendor= 0x16d8 -DefaultProduct=0x6281 - TargetClass=0xff MessageContent="555342431234567824000000800008ff524445564348470000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:6803 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:6803 index 8679554091..f61e0ca5e0 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:6803 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:6803 @@ -1,11 +1,5 @@ -######################################################## -# C-motech D-50 (aka "CDU-680") +# C-motech D-50 (aka "CDU-680", "CNU-680") -DefaultVendor= 0x16d8 -DefaultProduct=0x6803 +TargetClass=0x02 -TargetClass=0xff - -CheckSuccess=20 - -MessageContent="555342431234567824000000800008ff524445564348470000000000000000" +MessageContent="555342431234567824000000800008ff524445564348473100000000000000" diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:6804 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:6804 new file mode 100644 index 0000000000..731b296a8e --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:6804 @@ -0,0 +1,6 @@ +# C-motech CDU-685a + +TargetClass=0xff + +MessageContent="555342431234567824000000800008ff524445564348470000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:700a b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:700a index b28152dca2..71cd9776f1 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:700a +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:700a @@ -1,11 +1,6 @@ -######################################################## # C-motech CHU-629S -DefaultVendor= 0x16d8 -DefaultProduct=0x700a - TargetClass=0xff -CheckSuccess=20 - MessageContent="55534243123456782400000080000dfe524445564348473d4e444953000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:700b b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:700b index f6b05eaa25..6e8179fc82 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:700b +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:700b @@ -1,12 +1,6 @@ -######################################################## # C-motech CHU-629S (Variant) -DefaultVendor= 0x16d8 -DefaultProduct=0x700b - TargetClass=0xff MessageContent="55534243123456782400000080000dfe524445564348473d4e444953000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:f000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:f000 index df7abde5cf..7c5f4cfeeb 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:f000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/16d8:f000 @@ -1,12 +1,7 @@ -######################################################## # C-motech CGU-628 (aka "Franklin Wireless CGU-628A" aka "4G Systems XS Stick W12") -DefaultVendor= 0x16d8 -DefaultProduct=0xf000 - TargetVendor= 0x16d8 TargetProduct= 0x6006 -CheckSuccess=20 - MessageContent="55534243d85dd88524000000800008ff524445564348470000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0115 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1726:1900 similarity index 61% copy from release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0115 copy to release/src/router/usbmodeswitch/data/usb_modeswitch.d/1726:1900 index 9d050022fb..c4e72eb33e 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0115 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1726:1900 @@ -1,15 +1,12 @@ ######################################################## -# ZTE MF651 +# Axesstel MV110 -DefaultVendor= 0x19d2 -DefaultProduct=0x0115 +DefaultVendor= 0x1726 +DefaultProduct=0x1900 -TargetVendor= 0x19d2 -TargetProduct= 0x0116 +TargetVendor= 0x1726 +TargetProduct= 0x1000 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - NeedResponse=1 - CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1726:f00e b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1726:f00e new file mode 100644 index 0000000000..f2a713232a --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1726:f00e @@ -0,0 +1,7 @@ +# Axesstel MU130 + +TargetVendor= 0x1726 +TargetProduct= 0xa000 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/198a:0003 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/198a:0003 index 5177a4d6f9..1f821eefb6 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/198a:0003 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/198a:0003 @@ -1,13 +1,7 @@ -######################################################## # JOA Telecom LM-700r -DefaultVendor= 0x198a -DefaultProduct=0x0003 - TargetVendor= 0x198a TargetProduct= 0x0002 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/198f:bccd b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/198f:bccd index 5f8fefe9e0..7e57caf793 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/198f:bccd +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/198f:bccd @@ -1,14 +1,8 @@ -######################################################## # Beceem BCSM250 -DefaultVendor= 0x198f -DefaultProduct=0xbccd - TargetVendor= 0x198f TargetProduct= 0x0220 -CheckSuccess=20 - MessageContent="55534243f0298d8124000000800006bc626563240000000000000000000000" NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 index d68e17a7e3..d4e9011fc0 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 @@ -1,13 +1,7 @@ -######################################################## # ZTE MU351 -DefaultVendor= 0x19d2 -DefaultProduct=0x0003 - TargetClass=0xff MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0013 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0013 index 8591aa112d..fe48633c04 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0013 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0013 @@ -1,14 +1,8 @@ -######################################################## # Vodafone/ZTE K3806-Z -DefaultVendor= 0x19d2 -DefaultProduct=0x0013 - TargetVendor= 0x19d2 TargetProduct= 0x0015 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0026 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0026 index 78a919a0f0..b728faa292 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0026 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0026 @@ -1,15 +1,9 @@ -######################################################## # ZTE AC581 -DefaultVendor= 0x19d2 -DefaultProduct=0x0026 - TargetVendor= 0x19d2 -TargetProduct= 0x0094 +TargetProductList="0094,0152" MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0026 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0031 similarity index 51% copy from release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0026 copy to release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0031 index 78a919a0f0..31ac6b43f1 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0026 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0031 @@ -1,15 +1,8 @@ -######################################################## -# ZTE AC581 - -DefaultVendor= 0x19d2 -DefaultProduct=0x0026 +# ZTE MF637 TargetVendor= 0x19d2 TargetProduct= 0x0094 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0040 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0040 index 15c4a92c82..758e1e40f6 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0040 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0040 @@ -1,13 +1,7 @@ -######################################################## # Vodafone (ZTE) K2525 -DefaultVendor= 0x19d2 -DefaultProduct=0x0040 - TargetVendor= 0x19d2 TargetProduct= 0x0022 MessageContent="5553424312345678000000000000061b000000030000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0053 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0053 index 64a31fe368..a843a4b889 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0053 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0053 @@ -1,9 +1,5 @@ -######################################################## # ZTE MF110 (Variant) -DefaultVendor= 0x19d2 -DefaultProduct=0x0053 - TargetVendor= 0x19d2 TargetProduct= 0x0031 @@ -12,5 +8,3 @@ MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0083 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0083 index d72c4658cd..80f4076d63 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0083 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0083 @@ -1,15 +1,8 @@ -######################################################## # ZTE MF110 (Variant) -DefaultVendor= 0x19d2 -DefaultProduct=0x0083 - TargetVendor= 0x19d2 TargetProduct= 0x0124 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0083 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0083:uPr=WCDMA similarity index 58% copy from release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0083 copy to release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0083:uPr=WCDMA index d72c4658cd..516dcd512d 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0083 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0083:uPr=WCDMA @@ -1,9 +1,5 @@ -######################################################## # ZTE MF110 (Variant) -DefaultVendor= 0x19d2 -DefaultProduct=0x0083 - TargetVendor= 0x19d2 TargetProduct= 0x0124 @@ -11,5 +7,3 @@ MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0101 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0101 index be4c98b240..69f096b942 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0101 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0101 @@ -1,9 +1,5 @@ -######################################################## # Vodafone (ZTE) K4505-Z -DefaultVendor= 0x19d2 -DefaultProduct=0x0101 - TargetVendor= 0x19d2 TargetProduct= 0x0104 @@ -11,5 +7,3 @@ MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0103 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0103 index eb75832649..ac2f9e557a 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0103 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0103 @@ -1,9 +1,5 @@ -######################################################## # ZTE MF112 -DefaultVendor= 0x19d2 -DefaultProduct=0x0103 - TargetVendor= 0x19d2 TargetProduct= 0x0031 @@ -12,5 +8,3 @@ MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0110 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0110 index 467a0498d2..6c94157a5c 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0110 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0110 @@ -1,15 +1,8 @@ -######################################################## # ZTE MF637 (Variant for Orange France) -DefaultVendor= 0x19d2 -DefaultProduct=0x0110 - TargetVendor= 0x19d2 TargetProduct= 0x0121 - MessageContent="5553424302000000000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0115 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0115 index 9d050022fb..1858d8893a 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0115 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0115 @@ -1,9 +1,5 @@ -######################################################## # ZTE MF651 -DefaultVendor= 0x19d2 -DefaultProduct=0x0115 - TargetVendor= 0x19d2 TargetProduct= 0x0116 @@ -11,5 +7,3 @@ MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0120 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0120 new file mode 100644 index 0000000000..d07d751dc9 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0120 @@ -0,0 +1,12 @@ +#ZTE-T A356 + +TargetVendor= 0x19d2 +TargetProduct= 0x0079 + +DetachStorageOnly=0 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + +Interface=0x00 + +NeedResponse=0 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0146 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0146 new file mode 100644 index 0000000000..609ab0b65a --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0146 @@ -0,0 +1,8 @@ +# ZTE MF652 (Variant) + +TargetVendor= 0x19d2 +TargetProduct= 0x0143 + +MessageContent="5553424312345679000000000000061b000000020000000000000000000000" +NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0149 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0149 index 2a5a3c48df..8f2b2c8907 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0149 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0149 @@ -1,9 +1,5 @@ -######################################################## # ZTE MF190 (Variant) -DefaultVendor= 0x19d2 -DefaultProduct=0x0149 - TargetVendor= 0x19d2 TargetProduct= 0x0124 @@ -12,5 +8,3 @@ MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" MessageContent3="55534243123456702000000080000c85010101180101010101000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0083 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0150 similarity index 50% copy from release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0083 copy to release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0150 index d72c4658cd..2ac583fe2b 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0083 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0150 @@ -1,15 +1,8 @@ -######################################################## -# ZTE MF110 (Variant) - -DefaultVendor= 0x19d2 -DefaultProduct=0x0083 +# ZTE MF656A, MF668A, MF669 TargetVendor= 0x19d2 TargetProduct= 0x0124 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1224 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0154 similarity index 56% copy from release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1224 copy to release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0154 index 5b3c198f75..7c07502ae8 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1224 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0154 @@ -1,15 +1,9 @@ -######################################################## -# ZTE MF190 - -DefaultVendor= 0x19d2 -DefaultProduct=0x1224 +# ZTE MF190 (Variant) and others TargetVendor= 0x19d2 -TargetProduct= 0x0082 +TargetProductList="0017,0117" MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0166 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0166 index d60a30af37..1239a20cd5 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0166 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0166 @@ -1,12 +1,7 @@ -#ZTE MF820 (aka MF821d) +# ZTE MF820 4G LTE -DefaultVendor= 0x19d2 -DefaultProduct= 0x0166 -TargetVendor= 0x19d2 -TargetProduct= 0x0167 +TargetVendor= 0x19d2 +TargetProduct= 0x0167 MessageContent="55534243123456782400000080000685000000240000000000000000000000" -NeedResponse=1 - -CheckSuccess=20 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0169 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0169 new file mode 100644 index 0000000000..e16837f18f --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0169 @@ -0,0 +1,8 @@ +# ZTE A371B + +TargetVendor= 0x19d2 +TargetProduct= 0x0170 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" +NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0266 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0266 new file mode 100644 index 0000000000..6b7d4666b8 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0266 @@ -0,0 +1,7 @@ +# Onda MT8205 LTE + +TargetVendor= 0x19d2 +TargetProduct= 0x0265 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0325 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0325 new file mode 100644 index 0000000000..c1f1242ea3 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0325 @@ -0,0 +1,7 @@ +# ZTE MF821D + +TargetVendor= 0x12d1 +TargetProduct= 0x0326 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1001 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1001 dissimilarity index 65% index fb13a068dc..de78372034 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1001 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1001 @@ -1,20 +1,9 @@ -######################################################## -# Vodafone (ZTE) K3805-Z -# -# Note: -# This device has multiple USB profiles. Depending upon how it is flipped -# from storage mode to modem mode determines its final PID and the packages -# shown on its ISO CD image. - -DefaultVendor= 0x19d2 -DefaultProduct=0x1001 - -TargetVendor= 0x19d2 -TargetProduct= 0x1003 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - -CheckSuccess=20 - +# Vodafone (ZTE) K3805-Z + +TargetVendor= 0x19d2 +TargetProduct= 0x1003 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + +NeedResponse=1 +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1007 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1007 index 3ef2180628..5b3a41b230 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1007 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1007 @@ -1,9 +1,5 @@ -######################################################## # Vodafone (ZTE) K3570-Z -DefaultVendor= 0x19d2 -DefaultProduct=0x1007 - TargetVendor= 0x19d2 TargetProduct= 0x1008 @@ -11,5 +7,3 @@ MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1009 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1009 index bf70c65a5f..883a895c52 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1009 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1009 @@ -1,9 +1,5 @@ -######################################################## # Vodafone (ZTE) K3571-Z -DefaultVendor= 0x19d2 -DefaultProduct=0x1009 - TargetVendor= 0x19d2 TargetProduct= 0x1010 @@ -11,5 +7,3 @@ MessageContent="5553424312345678000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1013 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1013 dissimilarity index 65% index d92a768e73..c94e90fde6 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1013 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1013 @@ -1,20 +1,9 @@ -######################################################## -# Vodafone (ZTE) K3806-Z -# -# Note: -# This device has multiple USB profiles. Depending upon how it is flipped -# from storage mode to modem mode determines its final PID and the packages -# shown on its ISO CD image. - -DefaultVendor= 0x19d2 -DefaultProduct=0x1013 - -TargetVendor= 0x19d2 -TargetProduct= 0x1015 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - -CheckSuccess=20 - +# Vodafone (ZTE) K3806-Z + +TargetVendor= 0x19d2 +TargetProduct= 0x1015 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + +NeedResponse=1 +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1017 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1017 new file mode 100644 index 0000000000..01c97f3bec --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1017 @@ -0,0 +1,7 @@ +# Vodafone K5006Z (MF821) + +TargetVendor= 0x12d1 +TargetProduct= 0x1018 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1171 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1171 dissimilarity index 65% index 566542d850..d4938985e9 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1171 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1171 @@ -1,19 +1,9 @@ -######################################################## -# Vodafone (ZTE) K4510-Z -# -# Note: -# This device has multiple USB profiles. Depending upon how it is flipped -# from storage mode to modem mode determines its final PID and the packages -# shown on its ISO CD image. - -DefaultVendor= 0x19d2 -DefaultProduct=0x1171 - -TargetVendor= 0x19d2 -TargetProduct= 0x1173 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - -CheckSuccess=20 +# Vodafone (ZTE) K4510-Z + +TargetVendor= 0x19d2 +TargetProduct= 0x1173 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + +NeedResponse=1 +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1175 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1175 dissimilarity index 71% index 6c6e552e86..9a6573ceb7 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1175 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1175 @@ -1,19 +1,9 @@ -######################################################## -# Vodafone (ZTE) K3770-Z -# -# Note: -# This device has multiple USB profiles. Depending upon how it is flipped -# from storage mode to modem mode determines its final PID and the packages -# shown on its ISO CD image. - -DefaultVendor= 0x19d2 -DefaultProduct=0x1175 - -TargetVendor= 0x19d2 -TargetProduct= 0x1177 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - -CheckSuccess=20 +# Vodafone / ZTE K3770-Z + +TargetVendor= 0x19d2 +TargetProduct= 0x1177 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + +NeedResponse=1 +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1179 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1179 dissimilarity index 65% index 9b39514065..1e90fde468 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1179 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1179 @@ -1,19 +1,9 @@ -######################################################## -# Vodafone (ZTE) K3772-Z -# -# Note: -# This device has multiple USB profiles. Depending upon how it is flipped -# from storage mode to modem mode determines its final PID and the packages -# shown on its ISO CD image. - -DefaultVendor= 0x19d2 -DefaultProduct=0x1179 - -TargetVendor= 0x19d2 -TargetProduct= 0x1181 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - -CheckSuccess=20 +# Vodafone (ZTE) K3772-Z + +TargetVendor= 0x19d2 +TargetProduct= 0x1181 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + +NeedResponse=1 +NoDriverLoading=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1201 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1201 index b81f9e5dc8..b684a3c4ac 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1201 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1201 @@ -1,13 +1,7 @@ -######################################################## # ZTE MF691 (T-Mobile Rocket 2.0) -DefaultVendor= 0x19d2 -DefaultProduct=0x1201 - TargetVendor= 0x19d2 TargetProduct= 0x1203 MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1216 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1216 index 219eb7dd7d..3ed0c444e8 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1216 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1216 @@ -1,13 +1,6 @@ -######################################################## -# ZTE MF192 - -DefaultVendor= 0x19d2 -DefaultProduct=0x1216 +# ZTE MF192+ TargetVendor= 0x19d2 -TargetProduct= 0x1218 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -CheckSuccess=20 +TargetProduct= 0x1217 +MessageContent="555342430800498200000000000010ff000000000000000000000000000000" diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1224 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1224 index 5b3c198f75..8e5d5c1b31 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1224 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1224 @@ -1,9 +1,5 @@ -######################################################## # ZTE MF190 -DefaultVendor= 0x19d2 -DefaultProduct=0x1224 - TargetVendor= 0x19d2 TargetProduct= 0x0082 @@ -11,5 +7,3 @@ MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1227 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1227 new file mode 100644 index 0000000000..59dacb1d01 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1227 @@ -0,0 +1,8 @@ +# ZTE MF680 + +TargetVendor= 0x19d2 +TargetProduct= 0x1252 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" +NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1514 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1514 index 85baa6a9ee..7902fd56af 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1514 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1514 @@ -1,10 +1,7 @@ -# ZTE MF192 variant x (T-Mobile/EE UK) aka ZTE MF195 +# ZTE MF192 (yet annother variant) -DefaultVendor= 0x19d2 -DefaultProduct= 0x1514 - -TargetVendor= 0x19d2 +TargetVendor= 0x19d2 TargetProduct= 0x1515 -MessageEndpoint = 0x01 MessageContent="5553424348c4758600000000000010ff000000000000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1517 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1517 index 8df6809138..7d5607d98d 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1517 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1517 @@ -1,15 +1,9 @@ -######################################################## -# ZTE MF192 -# by C!chy +# ZTE MF192 (Variant) -DefaultVendor= 0x19d2 -DefaultProduct=0x1517 - -TargetVendor= 0x19d2 +TargetVendor= 0x19d2 TargetProduct= 0x1519 MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1520 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1520 new file mode 100644 index 0000000000..d23eedbdaf --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1520 @@ -0,0 +1,8 @@ +# ZTE MF652 + +TargetVendor= 0x19d2 +TargetProduct= 0x0142 + +MessageContent="5553424312345679000000000000061b000000020000000000000000000000" +NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1523 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1523 new file mode 100644 index 0000000000..63c5362d27 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1523 @@ -0,0 +1,8 @@ +# ZTE MF591 TMobile + +TargetVendor= 0x19d2 +TargetProduct= 0x1525 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + +NeedResponse=1 \ No newline at end of file diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1528 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1528 new file mode 100644 index 0000000000..34e898b155 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1528 @@ -0,0 +1,8 @@ +# ZTE MF196 + +TargetVendor= 0x19d2 +TargetProduct= 0x1527 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" +NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1224 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1542 similarity index 56% copy from release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1224 copy to release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1542 index 5b3c198f75..5ed0e410de 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1224 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:1542 @@ -1,15 +1,9 @@ -######################################################## -# ZTE MF190 - -DefaultVendor= 0x19d2 -DefaultProduct=0x1224 +# ZTE MF190J TargetVendor= 0x19d2 -TargetProduct= 0x0082 +TargetProduct= 0x1544 MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:2000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:2000 index 3980a579d0..54565b72c4 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:2000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:2000 @@ -1,11 +1,7 @@ -######################################################## # ZTE devices -DefaultVendor= 0x19d2 -DefaultProduct= 0x2000 - TargetVendor= 0x19d2 -TargetProductList="0001,0002,0015,0016,0017,0031,0037,0052,0055,0063,0064,0066,0091,0108,0117,0128,2002" +TargetProductList="0001,0002,0015,0016,0017,0031,0037,0052,0055,0061,0063,0064,0066,0091,0108,0117,0128,0157,0177,1402,2002,2003" MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" @@ -13,5 +9,3 @@ MessageContent3="55534243123456702000000080000c85010101180101010101000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:bccd b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:bccd index c323024c6c..a144d0676f 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:bccd +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:bccd @@ -1,14 +1,8 @@ -######################################################## # ZTE AX226 WiMax -DefaultVendor= 0x19d2 -DefaultProduct=0xbccd - TargetVendor= 0x19d2 TargetProduct= 0x0172 MessageContent="555342431234567824000000800006bc626563240000000000000000000000" NoDriverLoading=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:ffde b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:ffde new file mode 100644 index 0000000000..b5e1464721 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:ffde @@ -0,0 +1,7 @@ +# ZTE AC682 (a.k.a. SmartFren Connex) + +TargetVendor= 0x19d2 +TargetProduct= 0xffdd + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:ffe6 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:ffe6 index 86c7524025..afca331ba0 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:ffe6 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:ffe6 @@ -1,13 +1,7 @@ -######################################################## # ZTE "ffe" devices 1 (e.g. Cricket A605) -DefaultVendor= 0x19d2 -DefaultProduct=0xffe6 - TargetVendor= 0x19d2 TargetProduct= 0xffe5 MessageContent="5553424330f4cf8124000000800108df200000000000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:fff5 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:fff5 index 1c6d9423b2..37ce5a875a 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:fff5 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:fff5 @@ -1,13 +1,7 @@ -######################################################## # ZTE "fff" devices 1 -DefaultVendor= 0x19d2 -DefaultProduct=0xfff5 - TargetVendor= 0x19d2 -TargetProductList="fff1,ffff" +TargetProductList="fff1,fffe,ffff" MessageContent="5553424312345678c00000008000069f030000000000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:fff6 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:fff6 index aa8c8e91d6..9e9ad07278 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:fff6 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:fff6 @@ -1,13 +1,7 @@ -######################################################## # ZTE "fff" devices 2 -DefaultVendor= 0x19d2 -DefaultProduct=0xfff6 - TargetVendor= 0x19d2 TargetProduct= 0xfff1 MessageContent="5553424312345678c00000008000069f030000000000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1a8d:1000 similarity index 52% copy from release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 copy to release/src/router/usbmodeswitch/data/usb_modeswitch.d/1a8d:1000 index d68e17a7e3..acb4ab6246 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1a8d:1000 @@ -1,13 +1,11 @@ -######################################################## -# ZTE MU351 +# BandRich BandLuxe C100, C120, C170, C270, C3xx, C508 -DefaultVendor= 0x19d2 -DefaultProduct=0x0003 - -TargetClass=0xff +TargetVendor= 0x1a8d +TargetProductList="1002,1009,100d,2006" MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" +ReleaseDelay=4000 -CheckSuccess=20 +NeedResponse=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1a8d:2000 similarity index 52% copy from release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 copy to release/src/router/usbmodeswitch/data/usb_modeswitch.d/1a8d:2000 index d68e17a7e3..41ff8425c6 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1a8d:2000 @@ -1,13 +1,9 @@ -######################################################## -# ZTE MU351 +# BandRich BandLuxe C339 -DefaultVendor= 0x19d2 -DefaultProduct=0x0003 - -TargetClass=0xff +TargetVendor= 0x1a8d +TargetProduct= 0x2006 MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -CheckSuccess=20 +NeedResponse=1 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ab7:5700 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ab7:5700 index 8ad5a28fb1..119b6f6cb2 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ab7:5700 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ab7:5700 @@ -1,12 +1,7 @@ -######################################################## -# Hummer DTM5731 - -DefaultVendor= 0x1ab7 -DefaultProduct=0x5700 +# Hummer DTM5731, Aircard 901 TargetVendor= 0x1ab7 -TargetProduct= 0x5731 - -CheckSuccess=20 +TargetProductList="2000,5731" MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1b7d:0700 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1b7d:0700 index 907320b501..9011969ee1 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1b7d:0700 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1b7d:0700 @@ -1,12 +1,7 @@ -######################################################## # EpiValley SEC-7089 (featured by Alegro and Starcomms / iZAP) -DefaultVendor= 0x1b7d -DefaultProduct=0x0700 - TargetVendor= 0x1b7d TargetProduct= 0x0001 -CheckSuccess=20 - MessageContent="555342431234567824000000800008FF05B112AEE102000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:000f b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:000f new file mode 100644 index 0000000000..15a719b1b9 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:000f @@ -0,0 +1,6 @@ +# ETCOM E300 + +TargetVendor= 0x1bbb +TargetProduct= 0x000f + +MessageContent="5553424328ae1a85000000000000061b000000020000000000000000000000" diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:00ca b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:00ca new file mode 100644 index 0000000000..74b580dda1 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:00ca @@ -0,0 +1,6 @@ +# Alcatel OT-X080C + +TargetClass=0xff + +MessageContent="55534243123456788000000080000606f50402527000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:f000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:f000 index fbced43340..72634eb354 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:f000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:f000 @@ -1,12 +1,7 @@ -######################################################## -# Alcatel X200/X200L/X060S - -DefaultVendor= 0x1bbb -DefaultProduct=0xf000 +# Alcatel X200/X200L/X060S, Archos G9 3G Key TargetVendor= 0x1bbb -TargetProductList="0000,0017" - -CheckSuccess=20 +TargetProductList="0000,0017,00b7" MessageContent="55534243123456788000000080000606f50402527000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:f017 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:f017 new file mode 100644 index 0000000000..5e34784c3f --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:f017 @@ -0,0 +1,7 @@ +# Alcatel OT-X220D, L100V + +TargetVendor= 0x1bbb +TargetProductList="0017,011e" + +MessageContent="55534243123456788000000080000606f50402527000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:f052 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:f052 new file mode 100644 index 0000000000..e3ad37cbc9 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1bbb:f052 @@ -0,0 +1,7 @@ +# Alcatel OT X220L + +TargetVendor= 0x1bbb +TargetProduct= 0x0052 + +MessageContent="55534243123456788000000080000606f50402527000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:1001 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:1001 index fa4ac3a60a..edd28b347a 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:1001 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:1001 @@ -1,12 +1,7 @@ -######################################################## # Alcatel One Touch X020 -DefaultVendor= 0x1c9e -DefaultProduct=0x1001 - TargetVendor= 0x1c9e TargetProduct= 0x6061 -CheckSuccess=20 - MessageContent="55534243123456780000000000000606f50402527000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:6061 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:6061 index 47a107a42c..d4786bc593 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:6061 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:6061 @@ -1,13 +1,6 @@ -######################################################## # Vibe 3G Modem -DefaultVendor= 0x1c9e -DefaultProduct=0x6061 - TargetClass=0xff - MessageContent="55534243123456780000000000000606f50402527000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9200 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9200 index 431828abdb..a3f856b778 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9200 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9200 @@ -1,12 +1,7 @@ -######################################################## # MyWave SW006 Sport Phone/Modem Combination -DefaultVendor= 0x1c9e -DefaultProduct=0x9200 - TargetVendor= 0x1c9e TargetProduct= 0x9202 -CheckSuccess=20 - MessageContent="55534243123456780000000000000606f50402527000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9800 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9800 index 5b7aaaf466..8e74f08ba4 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9800 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9800 @@ -1,12 +1,6 @@ -######################################################## # Longcheer SU9800 -DefaultVendor= 0x1c9e -DefaultProduct=0x9800 - TargetClass=0xff MessageContent="55534243123456788000000080000606f50402527000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:98ff b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:98ff new file mode 100644 index 0000000000..41eb6f8d44 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:98ff @@ -0,0 +1,7 @@ +# Telewell TW-3G HSPA+ + +TargetVendor= 0x1c9e +TargetProductList="9801,9801" + +MessageContent="55534243123456780000000080000606f50402527000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9e00 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9e00 index 7372814091..e9c33bb7cf 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9e00 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9e00 @@ -1,12 +1,6 @@ -######################################################## # BSNL Capitel -DefaultVendor= 0x1c9e -DefaultProduct=0x9e00 - TargetClass=0xff MessageContent="55534243123456780000000000000606f50402527000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9e08 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9e08 new file mode 100644 index 0000000000..bafea3fabc --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:9e08 @@ -0,0 +1,8 @@ +# Explay Slim + +TargetVendor= 0x1c9e +TargetProduct= 0x9e18 + +SierraMode=1 +NoDriverLoading=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:f000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:f000 index 377795c82e..b313eee0fb 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:f000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1c9e:f000 @@ -1,12 +1,7 @@ -######################################################## # MobiData MBD-200HU and others -DefaultVendor= 0x1c9e -DefaultProduct=0xf000 - TargetVendor= 0x1c9e TargetProductList="9000,9603,9605,9607" -CheckSuccess=20 - MessageContent="55534243123456788000000080000606f50402527000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1da5:f000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1da5:f000 new file mode 100644 index 0000000000..2c5524090b --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1da5:f000 @@ -0,0 +1,7 @@ +# Qisda H21 Flying Beetle + +TargetVendor= 0x1da5 +TargetProduct= 0x4512 + +QisdaMode=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1dd6:1000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1dd6:1000 index c0c9346eac..ac3d92b3f2 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1dd6:1000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1dd6:1000 @@ -1,13 +1,8 @@ -######################################################## # Solomon S3Gm-660 -DefaultVendor= 0x1dd6 -DefaultProduct=0x1000 - TargetVendor= 0x1dd6 TargetProduct= 0x1002 -CheckSuccess=20 - MessageContent="55534243123456781200000080000603000000020000000000000000000000" NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1de1:1101 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1de1:1101 new file mode 100644 index 0000000000..3b545afbee --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1de1:1101 @@ -0,0 +1,7 @@ +# Philips PicoPix 1020 Projector + +TargetVendor= 0x21e7 +TargetProduct= 0x000e + +MessageContent="55534243123456780000000000000cff020000000000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1e0e:f000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1e0e:f000 index 65d4f90b21..050ec96df6 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1e0e:f000 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1e0e:f000 @@ -1,13 +1,8 @@ -######################################################## # Option iCON 210, PROLiNK PHS100, Hyundai MB-810, A-Link 3GU -DefaultVendor= 0x1e0e -DefaultProduct=0xf000 - TargetVendor= 0x1e0e TargetProductList="9200,9000" -CheckSuccess=20 - MessageContent="555342431234567800000000000006bd000000020000000000000000000000" NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1e89:f000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1e89:f000 new file mode 100644 index 0000000000..bb0c6e9d9e --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1e89:f000 @@ -0,0 +1,7 @@ +# 3GO 3GO11 HSUPA + +TargetVendor= 0x1e89 +TargetProduct= 0x1a20 + +MessageContent="5553424312345678800000008000060619181a207000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1edf:6003 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1edf:6003 index fb6cf8b00d..271b7b0482 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1edf:6003 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1edf:6003 @@ -1,12 +1,6 @@ -####################################################### # AirPlus MCD-800 -DefaultVendor= 0x1edf -DefaultProduct=0x6003 - TargetClass=0xff Configuration=3 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0009 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0009 index 2b147972c4..320ec79314 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0009 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0009 @@ -1,14 +1,8 @@ -######################################################## # Onda MW833UP -DefaultVendor= 0x1ee8 -DefaultProduct=0x0009 - TargetVendor= 0x1ee8 TargetProduct= 0x000b MessageContent="555342431234567800000000000010ff000000000000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0013 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0013 index fc01f70cc6..c0bde6fa70 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0013 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0013 @@ -1,13 +1,8 @@ -######################################################## # Onda MW833UP -DefaultVendor= 0x1ee8 -DefaultProduct=0x0013 - TargetVendor= 0x1ee8 -TargetProduct= 0x0012 - -CheckSuccess=20 +TargetProductList="0012,0014" MessageContent="555342431234567800000000000010ff000000000000000000000000000000" NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0040 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0040 index 683edb24b1..0e772ad522 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0040 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0040 @@ -1,14 +1,8 @@ -######################################################## # Onda MW836UP-K -DefaultVendor= 0x1ee8 -DefaultProduct=0x0040 - TargetVendor= 0x1ee8 -TargetProduct= 0x003e +TargetProductList="003e,003f" MessageContent="555342431234567800000000000010ff000000000000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:004a b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:004a new file mode 100644 index 0000000000..845c34f9e1 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:004a @@ -0,0 +1,7 @@ +# Onda MDC655 + +TargetVendor= 0x1ee8 +TargetProduct= 0x0049 + +MessageContent="555342431234567800000000000010ff000000000000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0054 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0054 new file mode 100644 index 0000000000..70c40ef9fe --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0054 @@ -0,0 +1,7 @@ +# Onda MW875UP + +TargetVendor= 0x1ee8 +TargetProduct=0x0053 + +MessageContent="555342431234567800000000000010ff000000000000000000000000000000" +NeedResponse=1 \ No newline at end of file diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0060 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0060 new file mode 100644 index 0000000000..2d9c051c0c --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0060 @@ -0,0 +1,7 @@ +# Onda MSA 14.4 (TIM Brasil) + +TargetVendor= 0x1ee8 +TargetProduct= 0x005f + +MessageContent="555342431234567800000000000008ff000000000000030000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0063 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0063 new file mode 100644 index 0000000000..a8465fe5e1 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0063 @@ -0,0 +1,7 @@ +# Onda TM201 14.4 (TIM Italy) + +TargetVendor= 0x1ee8 +TargetProduct= 0x0064 + +MessageContent="555342431234567800000000000008FF000000000000030000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0068 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0068 new file mode 100644 index 0000000000..85045523b1 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1ee8:0068 @@ -0,0 +1,7 @@ +# Onda WM301 + +TargetVendor= 0x1ee8 +TargetProduct= 0x0069 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1f28:0021 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1f28:0021 index dcf5343b1d..e9eef32853 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1f28:0021 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1f28:0021 @@ -1,12 +1,7 @@ -######################################################## # Cricket A600 -DefaultVendor= 0x1f28 -DefaultProduct=0x0021 - TargetVendor= 0x1f28 TargetProduct= 0x0020 -CheckSuccess=20 - MessageContent="555342431234567824000000800108df200000000000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0032 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0032 index f5fbd24085..545256629c 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0032 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0032 @@ -1,13 +1,6 @@ -######################################################## # Franklin Wireless U210 (Variant) -DefaultVendor= 0x1fac -DefaultProduct=0x0032 - TargetClass=0xff - Configuration=2 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0130 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0130 index 1c28c0e561..8a9b9e2802 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0130 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0130 @@ -1,13 +1,7 @@ -######################################################## # Franklin Wireless U210 -DefaultVendor= 0x1fac -DefaultProduct=0x0130 - TargetVendor= 0x1fac TargetProduct= 0x0131 MessageContent="555342431234567824000000800108df200000000000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0150 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0150 new file mode 100644 index 0000000000..6217a9ed5c --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0150 @@ -0,0 +1,7 @@ +# Franklin Wireless U600 + +TargetVendor= 0x1fac +TargetProduct= 0x0151 + +MessageContent="555342431234567824000000800108df200000000000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0151 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0151 new file mode 100644 index 0000000000..4d7d4cf87b --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/1fac:0151 @@ -0,0 +1,5 @@ +# Franklin Wireless U600 + +TargetClass=0xff + +Configuration=2 \ No newline at end of file diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a706 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a706 new file mode 100644 index 0000000000..ee5b395d5d --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a706 @@ -0,0 +1,7 @@ +# D-Link DWM-156 (Variant) + +TargetVendor= 0x2001 +TargetProduct= 0x7d01 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a707 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a707 new file mode 100644 index 0000000000..4045b4ae52 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a707 @@ -0,0 +1,7 @@ +# D-Link DWM-156 (Variant) + +TargetVendor= 0x2001 +TargetProduct= 0x7d02 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a708 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a708 new file mode 100644 index 0000000000..2bdb5d96c4 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a708 @@ -0,0 +1,7 @@ +# D-Link DWM-156 (Variant) + +TargetVendor= 0x2001 +TargetProduct= 0x7d03 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a805 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a805 new file mode 100644 index 0000000000..a47260c57a --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a805 @@ -0,0 +1,7 @@ +# D-Link DWR-510 + +TargetVendor= 0x2001 +TargetProduct= 0x7e12 + +MessageContent="5553424308407086000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a80b b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a80b new file mode 100644 index 0000000000..4662dc83f3 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2001:a80b @@ -0,0 +1,7 @@ +# D-Link DWM-156 (Variant) + +TargetVendor= 0x2001 +TargetProduct= 0x7d00 + +MessageContent="555342431234567800000000000003f0010100000000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/201e:1023 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/201e:1023 new file mode 100644 index 0000000000..12923722f3 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/201e:1023 @@ -0,0 +1,9 @@ +# Haier CE682 (EVDO) + +TargetVendor= 0x201e +TargetProduct= 0x1022 + +MessageContent="55534243123456780000000000000600000000000000000000000000000000" +MessageContent2="5553424312345679c000000080000671030000000000000000000000000000" +NeedResponse=1 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/201e:2009 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/201e:2009 index e3e59da20f..2a110fbad8 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/201e:2009 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/201e:2009 @@ -1,15 +1,8 @@ -####################################################### # Haier CE 100 -DefaultVendor= 0x201e -DefaultProduct=0x2009 - TargetClass=0xff - MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" NeedResponse=1 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2020:0002 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2020:0002 new file mode 100644 index 0000000000..ab73f72799 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2020:0002 @@ -0,0 +1,7 @@ +# Mediatek MT6229 + +TargetVendor= 0x2020 +TargetProduct= 0x2000 + +MessageContent="555342430820298900000000000003f0010100000000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2020:f00e b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2020:f00e index 1a4faf7d26..34ac2b681a 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2020:f00e +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2020:f00e @@ -1,13 +1,7 @@ -########################################### # SpeedUp SU-8000U -DefaultVendor= 0x2020 -DefaultProduct=0xf00e - TargetVendor= 0x2020 TargetProductList="1005,1008" MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -CheckSuccess=20 - +WaitBefore=2 diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2077:1000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2077:1000 new file mode 100644 index 0000000000..dfe0a86865 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2077:1000 @@ -0,0 +1,7 @@ +# Changhong CH690 + +TargetVendor= 0x2077 +TargetProduct= 0x7001 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2077:f000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2077:f000 new file mode 100644 index 0000000000..caa2ad5749 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2077:f000 @@ -0,0 +1,7 @@ +# Telenet 3G dongle (T&W WU160) + +TargetVendor= 0x2077 +TargetProduct= 0x9000 + +MessageContent="5553424308902082000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/21f5:1000 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/21f5:1000 new file mode 100644 index 0000000000..45e8168c09 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/21f5:1000 @@ -0,0 +1,6 @@ +# StrongRising (China Telcom), Air FlexiNet + +TargetVendor= 0x21f5 +TargetProduct= 0x2008 + +MessageContent="5553424312345678c000000080000671010000000000000000000000000000" diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/22de:6801 similarity index 58% copy from release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 copy to release/src/router/usbmodeswitch/data/usb_modeswitch.d/22de:6801 index d68e17a7e3..06062b43e9 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/22de:6801 @@ -1,13 +1,7 @@ -######################################################## -# ZTE MU351 - -DefaultVendor= 0x19d2 -DefaultProduct=0x0003 +# WeTelecom WM-D200 TargetClass=0xff MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/22de:6803 similarity index 58% copy from release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 copy to release/src/router/usbmodeswitch/data/usb_modeswitch.d/22de:6803 index d68e17a7e3..f9d6e2bbd9 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/19d2:0003 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/22de:6803 @@ -1,13 +1,7 @@ -######################################################## -# ZTE MU351 - -DefaultVendor= 0x19d2 -DefaultProduct=0x0003 +# WeTelecom WM-D300 TargetClass=0xff MessageContent="5553424312345678000000000000061e000000000000000000000000000000" MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/22f4:0021 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/22f4:0021 new file mode 100644 index 0000000000..4395af2957 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/22f4:0021 @@ -0,0 +1,6 @@ +# Tata Photon+ + +TargetClass=0xff + +MessageContent="555342439f000000000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/230d:0001 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/230d:0001 index 04f603bd83..9b94c1c5af 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/230d:0001 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/230d:0001 @@ -1,12 +1,6 @@ -####################################################### # Linktop LW272/LW273 (BSNL Teracom) -DefaultVendor= 0x230d -DefaultProduct=0x0001 - TargetClass=0xff Configuration=3 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/230d:0007 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/230d:0007 index a2cf7a73b9..f62670dc43 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/230d:0007 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/230d:0007 @@ -1,12 +1,6 @@ -####################################################### # Visiontek 82GH 3G -DefaultVendor= 0x230d -DefaultProduct=0x0007 - TargetClass=0xff Configuration=3 -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/230d:0101 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/230d:0101 new file mode 100644 index 0000000000..0db23f0fe2 --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/230d:0101 @@ -0,0 +1,6 @@ +# Linktop LW272/LW273 + +TargetClass=0xff + +Configuration=2 + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2357:0200 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2357:0200 new file mode 100644 index 0000000000..37867721bb --- /dev/null +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/2357:0200 @@ -0,0 +1,7 @@ +# TP-Link MA180 + +TargetVendor= 0x2357 +TargetProduct= 0x0201 + +MessageContent="5553424312345678000000000000061b000000020000000000000000000000" + diff --git a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/8888:6500 b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/8888:6500 index 09eaa98e55..ab21de65c6 100644 --- a/release/src/router/usbmodeswitch/data/usb_modeswitch.d/8888:6500 +++ b/release/src/router/usbmodeswitch/data/usb_modeswitch.d/8888:6500 @@ -1,13 +1,7 @@ -######################################################## # Exiss Mobile E-190 series (made by C-motech) -DefaultVendor= 0x8888 -DefaultProduct=0x6500 - TargetVendor= 0x16d8 TargetProduct= 0x6533 MessageContent="5553424398e2c4812400000080000bff524445564348473d43440000000000" -CheckSuccess=20 - diff --git a/release/src/router/usbmodeswitch/device_reference.txt b/release/src/router/usbmodeswitch/device_reference.txt deleted file mode 100755 index 7a96bc5e61..0000000000 --- a/release/src/router/usbmodeswitch/device_reference.txt +++ /dev/null @@ -1,2604 +0,0 @@ -# -# Device and Configuration Reference (UTF-8 encoding used) -# -# Last modified: 2011-08-07 -# -# Collection of configurations for usb_modeswitch, a mode switching -# tool for controlling flip flop (multiple mode) USB devices -# -# Detailed instructions and a friendly forum on the homepage: -# http://www.draisberghof.de/usb_modeswitch -# -# You may want to check for a newer version of this file at: -# http://www.draisberghof.de/usb_modeswitch/device_reference.txt - - -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # -# -# Important! This file is just a reference! Use the data package! -# -# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - - -# In order to activate a device included here you might remove the -# comment signs (";") from the respective entry. -# -# It is rather recommended to copy device information to a different -# file, to ease editing work and oversight and to avoid possible -# conflicts by activating multiple entries. -# There is no guarantee that this file is error-free! -# -# The actual entries are further down, after the command reference. - - -# For custom settings: -# Numbers can be decimal or hexadecimal, MessageStrings MUST be -# hexadecimal without prepended "0x". Digits 9-16 in the known -# MessageStrings are arbitrary; I set them to "12345678" - -# What it all means (short command line flags appended): -# -# -# * DefaultVendor -v -# * DefaultProduct -p -# -# This is the ID the USB device shows after having been plugged in. -# The program needs this; if not found -> no action. -# -# -# * TargetVendor -V -# * TargetProduct -P -# -# These are the IDs of the USB device after successful mode switching. -# They are optional, but I recommend to provide them for better analysis. -# You definitely need them if you enable CheckSuccess (see below) -# -# -# * TargetProductList (file only) -# -# Like TargetProduct, but more than one possibility. Only used in automated -# config files (in /etc/usb_modeswitch.d). -# -# -# * TargetClass -C -# -# Some weird devices don't change IDs. They only switch the device class. -# If the device has the target class -> no action (and vice versa) -# -# -# * MessageEndpoint -m -# -# A kind of address inside the interface to which the "message" -# (the sequence that does the actual switching) is directed. -# Starting from version 0.9.7 the MessageEndpoint is autodetected -# if not given -# -# -# * MessageContent -M -# -# A hex string containing the "message" sequence; it will be -# sent as a USB bulk transfer -# -# -# * MessageContent2, ...3 -2/-3 -# -# Additional "messages". Use with "NeedResponse"! -# -# -# * ReleaseDelay -w -# -# Waiting time after message transfers. Helps with some sensitive devices -# -# -# * ResponseEndpoint -r -# * NeedResponse <0/1> -n -# -# Some devices were reported to require receiving the response of the -# bulk transfer to do the switching properly. Usually not needed. -# Starting from version 1.0.0 the ResponseEndpoint is autodetected -# if not given -# -# -# * DetachStorageOnly <0/1> -d -# -# Some devices just need to be detached from the usb-storage -# driver to initiate the mode switching. Using this feature -# instead of removing the whole usbstorage module keeps other -# storage devices working. -# -# -# * HuaweiMode <0/1> -H -# -# Some Huawei devices can be switched by a special control -# message. -# -# -# * SierraMode <0/1> -S -# -# Some Sierra devices can be switched by a special control -# message. -# -# -# * SonyMode <0/1> -O -# -# Some Sony-Ericsson devices can be switched by a special control -# message. This is experimental and might not have a stable result -# -# * KobilMode <0/1> -T -# -# Some Kobil devices can be switched by a special control -# message. -# -# -# * ResetUSB <0/1> -R -# -# Some devices need a rougher treatment. If the switching seems -# to do something (run udevmonitor), but your system does not reflect -# it, try this somewhat brutal method to do a reset after switching. -# Mind that if your device switched OK before, this will probably set -# it back to storage mode ... -# -# -# * Interface -i -# * Configuration -u -# * AltSetting -a -# -# More USB parameter to help with tricky devices and for doing lots -# of cruel experiments ... -# -## Note: -## AltSetting/Configuration changes and ResetUSB are executed after all -## other steps and can be combined or used on their own (e.g. a reset -## might have the same effect as a manual replug) -# -# -# * InquireDevice <0|1> -I (disables inquiry) -# -# The standard since 1.0.0 is to do a SCSI inquiry on the default device -# before other actions. This might be a future way to identify a device -# without ambiguities. If it causes trouble with your device, just disable. -# -# -# * CheckSuccess -s -# -# Check continuously if the switch succeeded for max seconds. -# First, an interface access test: most devices vanish after -# switching and can't be accessed anymore. -# Second, a recount of target devices: one more than at the initial -# count, at the same bus with a higher device number -> device -# switched fine. -# It's safe to give a higher value than needed; checking stops as -# soon as the target device is found -# -# -# * NoDriverLoading <0|1> (no command line parameter) -# -# The binary tells the wrapper script NOT to check for and initiate -# binding of the serial driver after switching. -# Mostly useful for non-modem devices -# -# -# -> All other entries are just ignored <- - -# Additional command line flags: -# -# Verbose output -W -# No output at all -q -# Other config file -c - -# For filling in all this information for an unknown device, -# see instructions and links on the homepage: -# http://www.draisberghof.de/usb_modeswitch -# -# If you find working codes and configurations, please contribute -# them! - - -####################################################### -# Option GlobeSurfer Icon (aka "Vodafone EasyBox") -# -# The message SHOULD be the same for all Option devices - -DefaultVendor= 0x05c6 -DefaultProduct= 0x1000 - -TargetVendor= 0x0af0 -TargetProduct= 0x6600 - -MessageContent="55534243123456780000000000000601000000000000000000000000000000" - - -####################################################### -# Option GlobeSurfer Icon 7.2 -# -# Contributor: The Pharscape Forum - -DefaultVendor= 0x05c6 -DefaultProduct= 0x1000 - -TargetVendor= 0x0af0 -TargetProduct= 0x6901 - -MessageContent="55534243123456780000000000000601000000000000000000000000000000" - - -######################################################## -# Option GlobeTrotter GT MAX 3.6 (aka "T-Mobile Web'n'walk Card Compact II") -# -# Contributor: Bernd Holzmüller - -DefaultVendor= 0x05c6 -DefaultProduct= 0x1000 - -TargetVendor= 0x0af0 -TargetProduct= 0x6600 - -MessageContent="55534243123456780000000000000601000000000000000000000000000000" - -# ResponseEndpoint=0x84 - -NeedResponse=1 - - -######################################################## -# Option GlobeTrotter GT MAX "7.2 Ready" -# -# Contributors: Lucas Benedicic, Morgwai Kotarbinski - -DefaultVendor= 0x05c6 -DefaultProduct= 0x1000 - -TargetVendor= 0x0af0 -TargetProduct= 0x6701 - -MessageContent="55534243123456780000000000000601000000000000000000000000000000" - - -######################################################## -# Option GlobeTrotter EXPRESS 7.2 (aka "T-Mobile wnw Express II") -# -# Contributor: Fridtjof Busse - -DefaultVendor= 0x05c6 -DefaultProduct= 0x1000 - -TargetVendor= 0x0af0 -TargetProduct= 0x6701 - -MessageContent="55534243123456780000000000000601000000000000000000000000000000" - -# Response reading needed according to one (1) report -NeedResponse=1 - - -####################################################### -# Option GlobeSurfer Icon 7.2, new firmware (HSO driver) -# -# Vendor/ProductID don't change when switching, only the device -# class does. Most new Option devices work with this. Just adapt the IDs. -# -# Option's HSO driver is part of the mainstream kernel; support is -# available at Pharscape (www.pharscape.org) -# -# Contributors: Paul Hardwick, Philip Aben - -DefaultVendor= 0x0af0 -DefaultProduct= 0x6911 - -TargetClass= 0xff - -MessageContent="55534243123456780000000000000601000000000000000000000000000000" - -# Here is a list with all currently supported Option default IDs: -# -# 0af0:6711 -# 0af0:6731 -# 0af0:6751 -# 0af0:6771 -# 0af0:6791 -# 0af0:6811 -# 0af0:6911 -# 0af0:6951 -# 0af0:6971 -# 0af0:7011 -# 0af0:7031 -# 0af0:7051 -# 0af0:7071 -# 0af0:7111 -# 0af0:7211 -# 0af0:7251 -# 0af0:7271 -# 0af0:7301 -# 0af0:7311 -# 0af0:7361 -# 0af0:7381 -# 0af0:7401 -# 0af0:7501 -# 0af0:7601 -# 0af0:7701 -# 0af0:7801 -# 0af0:7901 -# 0af0:7a01 -# 0af0:7a05 -# 0af0:8200 -# 0af0:8201 -# 0af0:8300 -# 0af0:8302 -# 0af0:8304 -# 0af0:8400 -# 0af0:8600 -# 0af0:8800 -# 0af0:8900 -# 0af0:9000 -# 0af0:c031 -# 0af0:c100 -# 0af0:d013 -# 0af0:d031 -# 0af0:d033 -# 0af0:d035 -# 0af0:d055 -# 0af0:d057 -# 0af0:d058 -# 0af0:d155 -# 0af0:d157 -# 0af0:d255 -# 0af0:d257 -# 0af0:d357 -# - - -######################################################## -# Option iCON 210 -# PROLiNK PHS100 (various looks) -# Hyundai Mobile MB-810 -# -# One report of switching with DetachStorageOnly. Needs at least -# a second to settle before binding to usbserial -# -# Contributor: wahlm, Peter Kraker, Pakdhetimin Sekum - -DefaultVendor= 0x1e0e -DefaultProduct= 0xf000 - -TargetVendor= 0x1e0e -TargetProduct= 0x9000 - -MessageContent="555342431234567800000000000006bd000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# AT&T USBConnect Quicksilver (made by Option, HSO driver) -# -# Contributor: sissie from Bullteam - -DefaultVendor= 0x0af0 -DefaultProduct= 0xd033 - -TargetClass= 0xff - -MessageContent="55534243785634120100000080000601000000000000000000000000000000" - - -######################################################## -# Huawei devices -# -# Contributor: Hans Kurent, Denis Sutter, Vincent Teoh - -DefaultVendor= 0x12d1 -DefaultProduct= 0x1003 - -TargetClass= 0xff - -HuaweiMode=1 - - -######################################################## -# Huawei E169 and others -# -# Contributor: Dale Lane - -DefaultVendor= 0x12d1 -DefaultProduct= 0x1001 - -TargetClass= 0xff - -# choose one of these: -DetachStorageOnly=1 -HuaweiMode=1 - - -######################################################## -# Huawei E180 -# -# Contributor: Tom Dawahare - -DefaultVendor= 0x12d1 -DefaultProduct= 0x1414 - -TargetClass= 0xff - -HuaweiMode=1 - - -######################################################## -# Huawei E630 -# -# There seem to be modem-only variants around - no storage, -# no switching -# -# Contributor: Joakim Wenrgren - -DefaultVendor= 0x1033 -DefaultProduct= 0x0035 - -TargetVendor= 0x12d1 -TargetProduct= 0x1003 - -HuaweiMode=1 - - -######################################################## -# ZTE MF620 (aka "Onda MH600HS") -# -# Probably works with DetachStorageOnly too -# -# Contributor: Flávio Moringa and others - -DefaultVendor= 0x19d2 -DefaultProduct= 0x2000 - -TargetVendor= 0x19d2 -TargetProductList="0001" - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# ZTE MF622 (aka "Onda MDC502HS"), MF100 and others -# -# Contributor: andylog - -DefaultVendor= 0x19d2 -DefaultProduct= 0x2000 - -TargetVendor= 0x19d2 -TargetProduct= 0x0002 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# ZTE MF628 -# -# Captured with "usbmon". Has a micro SD slot which can be -# activated alternatively -# -# Contributor: Alvaro Lopes - -DefaultVendor= 0x19d2 -DefaultProduct= 0x2000 - - -# To modem mode: - -TargetVendor= 0x19d2 -TargetProduct= 0x0015 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - -# To SD slot mode: - -TargetVendor= 0x05c6 -TargetProduct= 0x2001 - -MessageContent="55534243123456782000000080000a86010101180101010101000000000000" - -NeedResponse=1 - - -######################################################## -# ZTE MF622 (aka "Onda MDC502HS") -# ZTE MF626 -# ZTE MF628+ (tested version from Telia / Sweden) -# ZTE MF633 -# ZTE MF636 (aka "Telstra / BigPond 7.2 Mobile Card") -# ZTE MF637 -# and probably others not listed here -# -# Contributor: Joakim Wennergren and others - -DefaultVendor= 0x19d2 -DefaultProduct= 0x2000 - -TargetVendor= 0x19d2 -TargetProduct= 0x0031 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# ZTE MF638 (aka "Onda MDC525UP") -# -# Contributor: andylog - -DefaultVendor= 0x19d2 -DefaultProduct= 0x2000 - -TargetVendor= 0x19d2 -TargetProduct= 0x0037 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# ZTE AC8710 -# ZTE AC2726 -# and others -# -# Many new ZTE devices use this sequence. There are -# several ID combinations; check your default -# -# Contributor: Michael Khurtsiya, Amit Pundir and others - -DefaultVendor= 0x19d2 -DefaultProduct= 0xfff5 - -TargetVendor= 0x19d2 -TargetProduct= 0xffff - -# No. 2 - -DefaultVendor= 0x19d2 -DefaultProduct= 0xfff6 - -TargetVendor= 0x19d2 -TargetProduct= 0xfff1 - -# No. 3 - -DefaultVendor= 0x19d2 -DefaultProduct= 0xfff5 - -TargetVendor= 0x19d2 -TargetProduct= 0xfff1 - -MessageContent="5553424312345678c00000008000069f030000000000000000000000000000" - - -######################################################## -# ZTE AC2710 (EVDO) -# -# Contributor: Wasim Baig - -DefaultVendor= 0x19d2 -DefaultProduct= 0xfff5 - -TargetVendor= 0x19d2 -TargetProduct= 0xffff - -MessageContent="5553424312345678c00000008000069f010000000000000000000000000000" - -# Just for information: try it with the message from the AC8710 ... - - -######################################################## -# ZTE 6535-Z -# -# Contributor: David Taillandier - -DefaultVendor= 0x19d2 -DefaultProduct= 0x2000 - -TargetVendor= 0x19d2 -TargetProduct= 0x0052 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# ONDA MT503HS -# -# Contributor: Lucio Asnaghi a.k.a. kRAkEn/gORe - -DefaultVendor= 0x19d2 -DefaultProduct= 0x2000 - -TargetVendor= 0x19d2 -TargetProduct= 0x0002 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# ONDA MT505UP (most likely a ZTE model) -# -# Contributor: Alex Scortegagna - -DefaultVendor= 0x19d2 -DefaultProduct= 0x2000 - -TargetVendor= 0x19d2 -TargetProduct= 0x0002 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Novatel Wireless Ovation MC950D HSUPA -# Novatel Wireless Merlin XU950D -# Novatel Wireless Ovation 930D -# -# Contributor: Razvan Dragomirescu, Mike Kirk - -DefaultVendor= 0x1410 -DefaultProduct= 0x5010 - -TargetVendor= 0x1410 -TargetProduct= 0x4400 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -NeedResponse=1 - - -######################################################## -# Novatel U727 USB modem -# -# Modem only versions (no switching required) are around. -# -# Contributor: Chris Thielen - -DefaultVendor= 0x1410 -DefaultProduct= 0x5010 - -TargetVendor= 0x1410 -TargetProduct= 0x4100 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -NeedResponse=1 - - -######################################################## -# Novatel MC990D -# -# Contributor: Joakim Wennergren - -DefaultVendor= 0x1410 -DefaultProduct= 0x5020 - -Interface= 5 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -NeedResponse=1 - - -######################################################## -# Novatel U760 USB modem -# -# Contributor: Richard Laager - -DefaultVendor= 0x1410 -DefaultProduct= 0x5030 - -TargetVendor= 0x1410 -TargetProduct= 0x6000 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -NeedResponse=1 - - -######################################################## -# Alcatel One Touch X020 (aka OT-X020, aka MBD-100HU, aka Nuton 3.5G), works with Emobile D11LC -# Alcatel One Touch X030 (aka OT-X030, aka Nuton NT36HD) -# -# Contributor: Aleksandar Samardzic, Marcelo Fernandez - -DefaultVendor= 0x1c9e -DefaultProduct= 0x1001 - -TargetVendor= 0x1c9e -TargetProduct= 0x6061 - -MessageContent="55534243123456780000000000000606f50402527000000000000000000000" - - -######################################################## -# Alcatel X200/X060S - -DefaultVendor= 0x1bbb -DefaultProduct= 0xf000 - -TargetVendor= 0x1bbb -TargetProduct= 0x0000 - -MessageContent="55534243123456788000000080000606f50402527000000000000000000000" - - -######################################################## -# AnyDATA ADU-500A, ADU-510A, ADU-510L, ADU-520A -# -# This ID gets an "Option" treatment by newer kernels. -# Use module option "option_zero_cd=2" with usb-storage. -# A kernel patch to fix the problem is pending -# -# Contributor: Vladimir Poluektov, Gabriel Smolar - -DefaultVendor= 0x05c6 -DefaultProduct= 0x1000 - -TargetVendor= 0x16d5 -TargetProduct= 0x6502 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -NeedResponse=1 - - -######################################################## -# BandLuxe C120 -# -# Reportedly needs several switch calls in a certain order. See -# http://www.draisberghof.de/usb_modeswitch/bb/viewtopic.php?t=71 -# This might be a case to try the new ResetUSB function -# -# Contributor: Alexander Czigler - -DefaultVendor= 0x1a8d -DefaultProduct= 0x1000 - -TargetVendor= 0x1a8d -TargetProduct= 0x1002 - -MessageContent="55534243123456781200000080000603000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Solomon S3Gm-660 -# -# Same notes apply as with the BandLuxe C120 above -# -# Contributor: Alexander Czigler - -DefaultVendor= 0x1dd6 -DefaultProduct= 0x1000 - -TargetVendor= 0x1dd6 -TargetProduct= 0x1002 - -MessageContent="55534243123456781200000080000603000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# C-motech D-50 (aka "CDU-680") -# -# Interesting notes about the device in the forum post at -# http://www.draisberghof.de/usb_modeswitch/bb/viewtopic.php?t=69 -# -# Contributor: Alexander Czigler - -DefaultVendor= 0x16d8 -DefaultProduct= 0x6803 - -TargetVendor= 0x16d8 -TargetProduct= 0x680a - -MessageContent="555342431234567824000000800008ff524445564348470000000000000000" - - -######################################################## -# C-motech CGU-628 (aka "Franklin Wireless CGU-628A" aka "4G Systems XS Stick W12") -# -# Contributor: Mathias Picker - -DefaultVendor= 0x16d8 -DefaultProduct= 0xf000 - -TargetVendor= 0x16d8 -TargetProduct= 0x6006 - -MessageContent="55534243d85dd88524000000800008ff524445564348470000000000000000" - - -######################################################## -# Toshiba G450 -# -# Contributor: Mijail Anton - -DefaultVendor= 0x0930 -DefaultProduct= 0x0d46 - -TargetVendor= 0x0930 -TargetProduct= 0x0d45 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# UTStarcom UM175 (distributor "Alltel") -# -# Contributor: Mark A. Ziesemer - -DefaultVendor= 0x106c -DefaultProduct= 0x3b03 - -TargetVendor= 0x106c -TargetProduct= 0x3715 - -MessageContent="555342431234567824000000800008ff024445564348470000000000000000" - - -######################################################## -# Hummer DTM5731 -# -# Contributor: "paul" - -DefaultVendor= 0x1ab7 -DefaultProduct= 0x5700 - -TargetVendor= 0x1ab7 -TargetProduct= 0x5731 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# A-Link 3GU -# -# Contributor: A-Link Europe Ltd. - -DefaultVendor= 0x1e0e -DefaultProduct= 0xf000 - -TargetVendor= 0x1e0e -TargetProduct= 0x9200 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Sierra Wireless Compass 597 -# -# Contributor: Vincent Teoh - -DefaultVendor= 0x1199 -DefaultProduct= 0x0fff - -TargetVendor= 0x1199 -TargetProduct= 0x0023 - -SierraMode=1 - - -######################################################## -# Sierra Wireless AirCard 881U (most likely 880U too) -# -# Contributor: Vincent Teoh - -DefaultVendor= 0x1199 -DefaultProduct= 0x0fff - -TargetVendor= 0x1199 -TargetProduct= 0x6856 - -SierraMode=1 - - -######################################################## -# Sony Ericsson MD400 -# -# Special procedure, takes around 25 secs. on the whole - -DefaultVendor= 0x0fce -DefaultProduct= 0xd0e1 - -TargetClass= 0x02 - -SonyMode=1 -Configuration=2 - - -######################################################## -# LG LDU-1900D EV-DO (Rev. A) -# -# Recommended init command: ATE0V1&D2&C1S0=0 -# -# Contributor: Jérôme Oufella - -DefaultVendor= 0x1004 -DefaultProduct= 0x1000 - -MessageContent="55534243123456780000000000000aff554d53434847000000000000000000" - - -######################################################## -# Samsung SGH-Z810 USB (with microSD card) -# -# This ID gets a wrong "Option" treatment by newer kernels -# from 2.6.29 to 2.6.31 -# Use module option "option_zero_cd=2" with usb-storage. -# Problem fixed in 2.6.32 -# -# Contributor: A Friend - -DefaultVendor= 0x05c6 -DefaultProduct= 0x1000 - -TargetVendor= 0x04e8 -TargetProduct= 0x6601 - -MessageContent="55534243123456780000000000000616000000000000000000000000000000" - - -######################################################## -# MobiData MBD-200HU -# -# Contributor: Stefan Olejnik - -DefaultVendor= 0x1c9e -DefaultProduct= 0xf000 - -TargetVendor= 0x1c9e -TargetProduct= 0x9000 - -MessageContent="55534243123456788000000080000606f50402527000000000000000000000" - - -######################################################## -# ST Mobile Connect HSUPA USB Modem -# -# Use /dev/ttyUSB2 for connecting -# -# Contributor: Vincent Teoh - -DefaultVendor= 0x1c9e -DefaultProduct= 0xf000 - -TargetVendor= 0x1c9e -TargetProduct= 0x9063 - -MessageContent="55534243123456788000000080000606f50402527000000000000000000000" - - -######################################################## -# MyWave SW006 Sport Phone/Modem Combination -# -# Has a card reader too, working fine -# -# Contributor: Aaron Guidazzi - -DefaultVendor= 0x1c9e -DefaultProduct= 0x9200 - -TargetVendor= 0x1c9e -TargetProduct= 0x9202 - -MessageContent="55534243123456780000000000000606f50402527000000000000000000000" - - -######################################################## -# Cricket A600 -# -# Switches to ACM device. Might need a ResetUSB after switching - or not -# -# Contributor: Jeffrey Hoogland - -DefaultVendor= 0x1f28 -DefaultProduct= 0x0021 - -TargetVendor= 0x1f28 -TargetProduct= 0x0020 - -MessageContent="555342431234567824000000800108df200000000000000000000000000000" - -# Afterwards (optional): "usb_modeswitch -v 0x1f28 -p 0x0020 -R" - - -######################################################## -# EpiValley SEC-7089 (featured by Alegro and Starcomms / iZAP) -# -# Contributor: Chris Wright - -DefaultVendor= 0x1b7d -DefaultProduct= 0x0700 - -TargetVendor= 0x1b7d -TargetProduct= 0x0001 - -MessageContent="555342431234567824000000800008FF05B112AEE102000000000000000000" - - -######################################################## -# Samsung U209 -# -# Has a modem and a storage device after switching -# -# Contributor: Arif Ahmed - -DefaultVendor= 0x04e8 -DefaultProduct= 0xf000 - -TargetVendor= 0x04e8 -TargetProduct= 0x6601 - -MessageContent="55534243123456780000000000000616000000000000000000000000000000" - - -######################################################## -# Huawei E270+ (HSPA+ modem) -# Huawei E1762 -# Huawei E1820 -# -# Contributor: Paranoid Paranoia - -DefaultVendor= 0x12d1 -DefaultProduct= 0x1446 - -TargetVendor= 0x12d1 -TargetProduct= 0x14ac - -MessageContent="55534243123456780000000000000011060000000000000000000000000000" - - -######################################################## -# Huawei E1550 -# Huawei E1750 -# -# Contributor: Anders Blomdell, Ahmed Soliman - -DefaultVendor= 0x12d1 -DefaultProduct= 0x1446 - -TargetVendor= 0x12d1 -TargetProduct= 0x1001 - -MessageContent="55534243123456780000000000000011060000000000000000000000000000" - - -######################################################## -# ZTE K3520-Z -# -# Contributor: Paul McDermott - -DefaultVendor= 0x19d2 -DefaultProduct= 0x2000 - -TargetVendor= 0x19d2 -TargetProduct= 0x0055 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# MobiData MBD-200HU (aka 4G XS Stick W10/W14, aka Micromax MMX 300G, -# aka ChinaBird CBCPL68) -# -# Contributor: Chris - -DefaultVendor= 0x1c9e -DefaultProduct= 0xf000 - -TargetVendor= 0x1c9e -TargetProduct= 0x9603 - -MessageContent="55534243123456788000000080000606f50402527000000000000000000000" - - -######################################################## -# D-Link DWM-162-U5, Micromax MMX 300c -# -# Contributor: Zhang Le - -DefaultVendor= 0x05c6 -DefaultProduct= 0x2001 - -TargetVendor= 0x1e0e -TargetProductList="ce16,cefe" - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Novatel MC760 3G -# -# Contributor: Matt Roberds - -DefaultVendor= 0x1410 -DefaultProduct= 0x5031 - -TargetVendor= 0x1410 -TargetProduct= 0x6002 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# ZTE MF110 (Variant) -# -# Contributor: Moritz Grosse-Wentrup - -DefaultVendor= 0x19d2 -DefaultProduct= 0x0053 - -TargetVendor= 0x19d2 -TargetProduct= 0x0031 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -MessageContent2="55534243876543212000000080000c85010101180101010101000000000000" - -NeedResponse=1 - - -######################################################## -# Philips TalkTalk (NXP Semiconductors "Dragonfly") -# - -DefaultVendor= 0x0471 -DefaultProduct= 0x1237 - -TargetVendor= 0x0471 -TargetProduct= 0x1234 - -MessageContent="5553424312345678000000000000061b000000030000000000000000000000" - -NeedResponse=1 - - -######################################################## -# HuaXing E600 (NXP Semiconductors "Dragonfly") -# -# Contributor: Emfox Zhou - -DefaultVendor= 0x0471 -DefaultProduct= 0x1237 - -TargetVendor= 0x0471 -TargetProduct= 0x1206 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - -Configuration=2 - - -######################################################## -# ZTE K3565 -# - -DefaultVendor= 0x19d2 -DefaultProduct= 0x2000 - -TargetVendor= 0x19d2 -TargetProduct= 0x0063 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Motorola 802.11 bg WLAN (TER/GUSB3-E) -# -# Contributor: Prashad Prashant - -DefaultVendor= 0x148f -DefaultProduct= 0x2578 - -TargetVendor= 0x148f -TargetProduct= 0x9021 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Huawei E1612 -# -# Contributor: Genar Codina - -DefaultVendor= 0x12d1 -DefaultProduct= 0x1446 - -TargetVendor= 0x12d1 -TargetProduct= 0x1406 - -MessageContent="55534243123456780000000000000011060000000000000000000000000000" - - -######################################################## -# Huawei E1690 -# Huawei E1692 -# Huawei E1762 -# -# Contributor: Carolin Latze and others - -DefaultVendor= 0x12d1 -DefaultProduct= 0x1446 - -TargetVendor= 0x12d1 -TargetProduct= 0x140c - -MessageContent="55534243123456780000000000000011060000000000000000000000000000" - - -######################################################## -# C-motech CHU-629S -# - -DefaultVendor= 0x16d8 -DefaultProduct= 0x700a - -TargetClass=0xff - -MessageContent="55534243123456782400000080000dfe524445564348473d4e444953000000" - - -######################################################## -# Sagem F@ST 9520-35-GLR -# -# Contributor: Yaroslav Levandovskiy - -DefaultVendor= 0x1076 -DefaultProduct= 0x7f40 - -TargetVendor= 0x1076 -TargetProduct= 0x7f00 - -GCTMode=1 - - -######################################################## -# Nokia CS-15 -# -# Contributor: Antti Turunen - -DefaultVendor= 0x0421 -DefaultProduct= 0x0610 - -TargetVendor= 0x0421 -TargetProduct= 0x0612 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Huawei K3765 -# -# Contributor: Felix Schwarz - -DefaultVendor= 0x12d1 -DefaultProduct= 0x1520 - -TargetVendor= 0x12d1 -TargetProduct= 0x1465 - -MessageContent="55534243123456780000000000000011060000000000000000000000000000" - - -######################################################## -# Huawei K4505 -# -# Contributor: Nikolaos Koutsianas - -DefaultVendor= 0x12d1 -DefaultProduct= 0x1521 - -TargetVendor= 0x12d1 -TargetProduct= 0x1464 - -MessageContent="55534243123456780000000000000011060000000000000000000000000000" - - -######################################### -# Vodafone MD950 (Wisue Technology) -# -# Contributor: Bob Williams - -DefaultVendor= 0x0471 -DefaultProduct= 0x1210 - -# The report said that giving the target IDs prevented -# the device from switching. Got to try it out ... - -#TargetVendor= 0x1dbc -#TargetProduct= 0x0005 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Siptune LM-75 ("LinuxModem") -# -# Contributor: Antti Turunen - -DefaultVendor= 0x05c6 -DefaultProduct= 0xf000 - -TargetVendor= 0x05c6 -TargetProduct= 0x9000 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - - -######################################################## -# Zydas ZD1211RW WLAN USB, Sphairon HomeLink 1202 (Variant 1) -# -# WLAN devices from Linux kernel - -DefaultVendor= 0x0ace -DefaultProduct= 0x2011 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Zydas ZD1211RW WLAN USB, Sphairon HomeLink 1202 (Variant 2) -# -# WLAN devices from Linux kernel - -DefaultVendor= 0x0ace -DefaultProduct= 0x20ff - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Vertex Wireless 100 Series -# -# Contributor: Reinis Danne - -DefaultVendor= 0x05c6 -DefaultProduct= 0x1000 - -TargetVendor= 0x1fe7 -TargetProduct= 0x0100 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# AVM Fritz!Wlan USB Stick N -# - -DefaultVendor= 0x057c -DefaultProduct= 0x84ff - -TargetVendor= 0x057c -TargetProduct= 0x8401 - -MessageContent="5553424312345678000000000000061b000000ff0000000000000000000000" - - -######################################################## -# InfoCert Business Key (SmartCard/Reader emulation) -# -# Contributor: Bicio - -DefaultVendor= 0x072f -DefaultProduct= 0x100d - -TargetVendor= 0x072f -TargetProduct= 0x90cc - -MessageContent="01b0000000000000000000000000000000000000000000000000000000000000" - - -######################################################## -# UTStarcom UM185E (distributor "Alltel") -# -# Contributor: Jason Smith - -DefaultVendor= 0x106c -DefaultProduct= 0x3b06 - -TargetVendor= 0x106c -TargetProduct= 0x3717 - -MessageContent="555342431234567824000000800008ff020000000000000000000000000000 - - -######################################################## -# ZTE AC581 -# -# Contributor: Alberto Maurizi - -DefaultVendor= 0x19d2 -DefaultProduct= 0x0026 - -TargetVendor= 0x19d2 -TargetProduct= 0x0094 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Huawei U7510 / U7517 -# -# Contributor: Isaac Salgado - -DefaultVendor= 0x12d1 -DefaultProduct= 0x101e - -TargetClass=0xff - -MessageContent="55534243123456780600000080000601000000000000000000000000000000" - - -######################################################## -# Beceem BCSM250 -# -# Contributor: Alexander Gordeev - -DefaultVendor= 0x198f -DefaultProduct=0xbccd - -TargetVendor= 0x198f -TargetProduct= 0x0220 - -MessageContent="555342431234567824000000800006bc626563240000000000000000000000" - - -######################################################## -# LG HDM-2100 (EVDO Rev.A USB modem) -# -# Contributor: Jérôme Oufella - -DefaultVendor= 0x1004 -DefaultProduct=0x607f - -TargetVendor= 0x1004 -TargetProduct= 0x6114 - -MessageContent="1201100102000040041014610000010200018006000100001200" - - -######################################################## -# Kyocera W06K CDMA modem -# -# Contributor: Ying-Hung Lo - -DefaultVendor= 0x0482 -DefaultProduct=0x024d - -Configuration=2 - - -######################################################## -# Digicom 8E4455 -# -# Contributor: Alberto Ciampini - -DefaultVendor= 0x1266 -DefaultProduct=0x1000 - -TargetVendor= 0x1266 -TargetProduct= 0x1009 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424387654321000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Sony Ericsson MD300 -# -# Contributor: Betavine Project - -DefaultVendor= 0x0fce -DefaultProduct=0xd0cf - -TargetClass=0x02 - -DetachStorageOnly=1 -Configuration=3 - - -######################################################## -# Vodafone (ZTE) K3805-Z -# -# Contributor: Betavine Project -# -# Note: -# This device has multiple USB profiles. Depending upon how it is flipped -# from storage mode to modem mode determines its final PID and the packages -# shown on its ISO CD image. - -DefaultVendor= 0x19d2 -DefaultProduct=0x1001 - -TargetVendor= 0x19d2 -TargetProduct= 0x1003 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Franklin Wireless U210 -# -# Contributor: Adam J. Porter - -DefaultVendor= 0x1fac -DefaultProduct=0x0130 - -TargetVendor= 0x1fac -TargetProduct= 0x0131 - -CheckSuccess=20 - -MessageContent="555342431234567824000000800108df200000000000000000000000000000" - - -######################################################## -# Alcatel X220L, X215S -# -# Contributor: John Watt - -DefaultVendor= 0x1bbb -DefaultProduct= 0xf000 - -TargetVendor= 0x1bbb -TargetProduct= 0x0017 - -MessageContent="55534243123456788000000080000606f50402527000000000000000000000" - - -######################################################## -# ZTE MF112 -# -# Contributors: Akos Ladanyi, John Talbut - -DefaultVendor= 0x19d2 -DefaultProduct= 0x0103 - -TargetVendor= 0x19d2 -TargetProduct= 0x0031 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -MessageContent2="55534243876543212000000080000c85010101180101010101000000000000" - -NeedResponse=1 - - -######################################################## -# Olivetti Olicard 100 and others -# -# Contributor: Nils Radtke - -DefaultVendor= 0x0b3c -DefaultProduct= 0xc700 - -TargetVendor= 0x0b3c -TargetProductList="c000,c001,c002" - -MessageContent="5553424312345678000000000000061b000000030000000000000000000000" - -NeedResponse=1 - - -######################################################## -# ZTE MF110 (Variant) -# -# Contributor: uusrlnx - -DefaultVendor= 0x19d2 -DefaultProduct= 0x0083 - -TargetVendor= 0x19d2 -TargetProduct= 0x0124 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# ZTE MU351 - -DefaultVendor= 0x19d2 -DefaultProduct= 0x0003 - -TargetClass=0xff - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# LG L-05A - -DefaultVendor= 0x1004 -DefaultProduct= 0x613a - -TargetVendor= 0x1004 -TargetProduct= 0x6124 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# LG LUU-2100TI (aka AT&T USBConnect Turbo) -# -# Contributor: Chris Jager - -DefaultVendor= 0x1004 -DefaultProduct= 0x613f - -TargetVendor= 0x1004 -TargetProduct= 0x6141 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# LG KP500 Cookie Phone - UNTESTED! Please report! -# -# Contributor: Oliver Mattos - -DefaultVendor= 0x1004 -DefaultProduct=0x607f - -TargetVendor= 0x1004 -TargetProduct= 0x6000 - -MessageContent="555342431234567803000000800006f1022000000000000000000000000000" -MessageContent2="555342438765432103000000800006f1010100000000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Royaltek Q110 - UNCONFIRMED! - -DefaultVendor= 0x1266 -DefaultProduct=0x1000 - -TargetVendor= 0x1266 -TargetProduct= 0x1009 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424387654321000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# BandRich BandLuxe C170, BandLuxe C270 - -DefaultVendor= 0x1a8d -DefaultProduct=0x1000 - -TargetVendor= 0x1a8d -TargetProduct= 0x1009 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" -ReleaseDelay=4000 - -NeedResponse=1 - - -######################################################## -# Vodafone (Huawei) K4605 -# -# Contributor: Betavine Project - -DefaultVendor= 0x12d1 -DefaultProduct=0x14c1 - -TargetVendor= 0x12d1 -TargetProduct= 0x14c6 - -CheckSuccess=20 - -MessageContent="55534243123456780000000000000011060000000000000000000000000000" - - -######################################################## -# Huawei R201 -# -# Contributor: Betavine Project - -DefaultVendor= 0x12d1 -DefaultProduct=0x1523 - -TargetVendor= 0x12d1 -TargetProduct= 0x1491 - -CheckSuccess=20 - -MessageContent="55534243123456780000000000000011060000000000000000000000000000" - - -######################################################## -# Atheros Wireless / Netgear WNDA3200 -# -# Contributor: Sujith Manoharan (Atheros) - -DefaultVendor= 0x0cf3 -DefaultProduct=0x20ff - -TargetVendor= 0x0cf3 -TargetProduct= 0x7010 - -CheckSuccess=10 -NoDriverLoading=1 - -MessageContent="5553424329000000000000000000061b000000020000000000000000000000" -NeedResponse=1 - - -######################################################## -# Onda MW833UP -# -# Contributor: Riccardo Sepe aka zeroidle - -DefaultVendor= 0x1ee8 -DefaultProduct=0x0013 - -TargetVendor= 0x1ee8 -TargetProduct= 0x0012 - -CheckSuccess=20 - -MessageContent="555342431234567800000000000010ff000000000000000000000000000000" -ResponseNeeded=1 - - -######################################################## -# Huawei U8110 / U8300 / Joy, Vodafone 845 (Android smartphone) -# -# Contributor: David Erosa García - -DefaultVendor= 0x12d1 -DefaultProduct=0x1031 - -TargetVendor= 0x12d1 -TargetProduct= 0x1035 - -CheckSuccess=20 - -MessageContent="55534243123456780600000080010a11060000000000000000000000000000" - -# for Android SDK -NoDriverLoading=1 - - -######################################################## -# Nokia CS-10 -# -# Contributor: Wacław Sierek - -DefaultVendor= 0x0421 -DefaultProduct=0x060c - -TargetVendor= 0x0421 -TargetProduct= 0x060e - -CheckSuccess=20 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - - -######################################################## -# ZTE WCDMA Stick from BNSL -# -# Contributor: Anoop John - -DefaultVendor= 0x19d2 -DefaultProduct= 0x2000 - -TargetVendor= 0x19d2 -TargetProduct= 0x0108 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# BSNL Capitel - -DefaultVendor= 0x1c9e -DefaultProduct= 0x9e00 - -TargetClass=0xff - -MessageContent="55534243123456780000000000000606f50402527000000000000000000000" - - -####################################################### -# Samsung GT-B3730 -# -# Contributor: Per Øyvind Karlsen - -DefaultVendor= 0x04e8 -DefaultProduct=0x689a - -TargetVendor= 0x04e8 -TargetProduct= 0x6889 - -MessageContent="55534243785634120100000080000601000000000000000000000000000000" - -####################################################### -# Kobil mIdentity 3G (1) -# -# Contributor: Filip Aben - -DefaultVendor= 0x0d46 -DefaultProduct=0x45a1 - -TargetVendor= 0x0d46 -TargetProduct= 0x45a9 - -KobilMode=1 - - -####################################################### -# Kobil mIdentity 3G (2) -# -# Contributor: Filip Aben - -DefaultVendor= 0x0d46 -DefaultProduct=0x45a5 - -TargetVendor= 0x0d46 -TargetProduct= 0x45ad - -KobilMode=1 - - -######################################################## -# Onda MW833UP -# -# Contributor: Enrico Mioso - -DefaultVendor= 0x1ee8 -DefaultProduct=0x0009 - -TargetVendor= 0x1ee8 -TargetProduct= 0x000b - -MessageContent="555342431234567800000000000010ff000000000000000000000000000000" -NeedResponse=1 - - -######################################################## -# Olivetti Olicard 145 -# -# Contributor: Pierre-Andre Cornillon - -DefaultVendor= 0x0b3c -DefaultProduct=0xf000 - -TargetVendor= 0x0b3c -TargetProduct= 0xc003 - -MessageContent="5553424312345678c000000080010606f50402527000000000000000000000" -NeedResponse=1 - - -####################################################### -# Huawei EC168C (from Zantel) -# -# Contributor: Åsmund Hjulstad - -DefaultVendor= 0x12d1 -DefaultProduct=0x1446 - -TargetVendor= 0x12d1 -TargetProduct= 0x1412 - -MessageContent="55534243123456780000000000000011060000000000000000000000000000" - - -####################################################### -# Nokia CS-17 -# -# Contributor: Juho Frits - -DefaultVendor= 0x0421 -DefaultProduct=0x0622 - -TargetVendor= 0x0421 -TargetProduct= 0x0623 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - - -####################################################### -# Nokia CS-18 -# -# Contributor: Thomas Behan - -DefaultVendor= 0x0421 -DefaultProduct=0x0627 - -TargetVendor= 0x0421 -TargetProduct= 0x0612 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - - -####################################################### -# Qtronix EVDO 3G Modem (for TianYi) -# -# Contributor: Alexey Charkov - -DefaultVendor= 0x05c7 -DefaultProduct=0x1000 - -TargetVendor= 0x05c7 -TargetProduct= 0x6000 - -MessageContent="5553424312345678c00000008000069f140000000000000000000000000000" - - -####################################################### -# GW D301 (Advinne AMC) -# - -DefaultVendor= 0x0fd1 -DefaultProduct=0x1000 - -TargetClass=0xff - -Configuration=3 - - -####################################################### -# LG AD600 -# -# Contributor: Shawn J. Goff - -DefaultVendor= 0x1004 -DefaultProduct=0x6190 - -TargetVendor= 0x1004 -TargetProduct= 0x61a7 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - - -####################################################### -# Pantech / UTStarcom UMW190 (Verizon) -# -# Contributor: Noel J. Bergman, Gus Esquivel - -DefaultVendor= 0x106c -DefaultProduct=0x3b05 - -TargetVendor= 0x106c -TargetProduct= 0x3716 - -MessageContent="555342431234567824000000800008ff020000000000000000000000000000" - - -####################################################### -# AirPlus MCD-800 -# - -DefaultVendor= 0x1edf -DefaultProduct=0x6003 - -TargetClass=0xff - -Configuration=3 - - -####################################################### -# Linktop LW272/LW273 (BSNL Teracom) -# -# Contributor: Amit Mendapara - -DefaultVendor= 0x230d -DefaultProduct=0x0001 - -TargetClass=0xff - -Configuration=3 - - -######################################################## -# MediaTek Wimax USB Card -# - -DefaultVendor= 0x0e8d -DefaultProduct=0x7109 - -TargetVendor= 0x0e8d -TargetProduct= 0x7118 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NoDriverLoading=1 - - -######################################################## -# C-motech CHU-628S -# -# Contributor: Jungwon Seo - -DefaultVendor= 0x16d8 -DefaultProduct=0x6281 - -TargetClass=0xff - -MessageContent="555342431234567824000000800008ff524445564348470000000000000000" - - -######################################################## -# HSDPA USB modem from dealextreme -# -# Contributor: Andrew 'Necromant' Andrianov - -DefaultVendor= 0x05c6 -DefaultProduct=0x2000 - -TargetVendor= 0x05c6 -TargetProduct= 0x0015 - -MessageContent="5553424368032c882400000080000612000000240000000000000000000000" - -NeedResponse=1 - -# This modem doesn't react fast - it pauses for some 30-40 seconds -CheckSuccess=40 - - -######################################################## -# Exiss Mobile E-190 series (made by C-motech) -# -# Contributor: Tri Mulya Saputra - -DefaultVendor= 0x8888 -DefaultProduct= 0x6500 - -TargetVendor= 0x16d8 -TargetProduct= 0x6533 - -MessageContent="5553424398e2c4812400000080000bff524445564348473d43440000000000" - - -######################################################## -# D-Link DWM-156 HSUPA 3.75G USB Modem -# F/W Ver. 1.05b01; upgr. at ftp://ftp.dlink.co.uk/dwm_3G/DWM-156/ -# -# Contributor: Fuego - -DefaultVendor= 0x07d1 -DefaultProduct=0xa800 - -TargetVendor= 0x07d1 -TargetProduct= 0x3e02 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - - -######################################################## -# Franklin Wireless U210 (Variant) - -DefaultVendor= 0x1fac -DefaultProduct=0x0032 - -Configuration=2 - - -####################################################### -# Haier CE 100 - -DefaultVendor= 0x201e -DefaultProduct=0x2009 - -TargetClass=0xff - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -####################################################### -# Huawei BM358 WiMAX - -DefaultVendor= 0x12d1 -DefaultProduct=0x380b - -TargetClass=0x02 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Huawei E352 -# -# Contributor: Antonio Talarico - -DefaultVendor= 0x12d1 -DefaultProduct=0x1449 - -TargetVendor= 0x12d1 -TargetProduct= 0x1444 - -MessageContent="55534243123456780000000000000011062000000100000000000000000000" - - -######################################################## -# Huawei ET8282 -# -# Contributor: Emfox Zhou - -DefaultVendor= 0x12d1 -DefaultProduct=0x1da1 - -DefaultVendor= 0x12d1 -TargetProduct= 0x1d09 - -HuaweiMode=1 - - -######################################################## -# Huawei V725 Phone (aka Vodafone 725) - -DefaultVendor= 0x12d1 -DefaultProduct=0x1009 - -TargetClass=0xff - -HuaweiMode=1 - - -######################################################## -# Onda MW836UP-K - -DefaultVendor= 0x1ee8 -DefaultProduct=0x0040 - -TargetVendor= 0x1ee8 -TargetProduct= 0x003e - -MessageContent="555342431234567800000000000010ff000000000000000000000000000000" - -NeedResponse=1 - - -######################################################## -# ZTE MF637 (Variant for Orange France) -# -# Contributor: David Vigier - -DefaultVendor= 0x19d2 -DefaultProduct=0x0110 - -TargetVendor= 0x19d2 -TargetProduct= 0x0121 - -MessageContent="5553424302000000000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# Prolink P2000 CDMA -# -# Test string: uMa=SSE - -DefaultVendor= 0x05c6 -DefaultProduct=0x1000 - -TargetVendor= 0x05c6 -TargetProduct= 0x6000 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" -NeedResponse=1 - - -######################################################## -# Vibe 3G Modem -# -# Contributor: Steven James Drinnan - -DefaultVendor= 0x1c9e -DefaultProduct=0x6061 - -TargetClass=0xff - -MessageContent="55534243123456780000000000000606f50402527000000000000000000000" - - -######################################################## -# Vodafone/ZTE K3806-Z - -DefaultVendor= 0x19d2 -DefaultProduct=0x0013 - -TargetVendor= 0x19d2 -TargetProduct= 0x0015 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - -NeedResponse=1 - -######################################################## -# T-Mobile NL (Huawei E352) -# -# Contributor: Alex Hermann - -DefaultVendor= 0x12d1 -DefaultProduct=0x14fe - -TargetVendor= 0x12d1 -TargetProduct= 0x1506 - -MessageContent="55534243123456780000000000000011062000000100000000000000000000" - - -######################################################## -# Huawei U8220, T-Mobile Pulse (Android smartphone) -# -# Contributor: Mobile Stream Team - -DefaultVendor= 0x12d1 -DefaultProduct=0x1030 - -TargetVendor= 0x12d1 -TargetProduct= 0x1034 - -MessageContent="55534243123456780600000080010a11060000000000000000000000000000" - -# for Android SDK -NoDriverLoading=1 - - -######################################################## -# D-Link DWM-156 HSUPA 3.75G USB Modem -# -# Contributor: Ari Suutari - -DefaultVendor= 0x07d1 -DefaultProduct=0xa804 - -TargetVendor= 0x07d1 -TargetProduct= 0x7e11 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - - -######################################################## -# Sony Ericsson MD400G (Rogers) -# -# Contributor: Kjartan Adolfsson - -DefaultVendor= 0x0fce -DefaultProduct= 0xd103 - -TargetClass= 0x02 - -SonyMode=1 - - -######################################################## -# ZTE "ffe" devices 1 (e.g. Cricket A605) -# -# Contributor: Matt Eaton - -DefaultVendor= 0x19d2 -DefaultProduct=0xffe6 - -TargetVendor= 0x19d2 -TargetProduct= 0xffe5 - -MessageContent="5553424330f4cf8124000000800108df200000000000000000000000000000" - - -######################################################## -# ZTE MF190 -# -# Contributor: Eshwar Andhavarapu - -DefaultVendor= 0x19d2 -DefaultProduct= 0x1224 - -TargetVendor= 0x19d2 -TargetProduct= 0x0082 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" - -NeedResponse=1 - - -######################################################## -# JOA Telecom LM-700r - -DefaultVendor= 0x198a -DefaultProduct=0x0003 - -TargetVendor= 0x198a -TargetProduct= 0x0002 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - - -######################################################## -# Huawei E173 (Viettel 3G) - -TargetVendor= 0x12d1 -TargetProduct= 0x14a8 - -DefaultVendor= 0x12d1 -DefaultProduct=0x14b5 - -MessageContent="55534243123456780000000000000011062000000100000000000000000000" - - -######################################################## -# C-motech CHU-629S (Variant) -# -# Contributor: "wlsloi" - -DefaultVendor= 0x16d8 -DefaultProduct=0x700b - -TargetClass=0xff - -MessageContent="55534243123456782400000080000dfe524445564348473d4e444953000000" - - -######################################################## -# ZTE MF691 (T-Mobile Rocket 2.0) -# -# Contributor: Michael Fogel - -DefaultVendor= 0x19d2 -DefaultProduct=0x1201 - -TargetVendor= 0x19d2 -TargetProduct= 0x1203 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - - -######################################################## -# ZTE MF192 - -DefaultVendor= 0x19d2 -DefaultProduct=0x1216 - -TargetVendor= 0x19d2 -TargetProduct= 0x1218 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - - -######################################################## -# ZTE MF190 (Variant) -# -# Contributor: Jean-Baptiste Dubois - -DefaultVendor= 0x19d2 -DefaultProduct=0x0149 - -TargetVendor= 0x19d2 -TargetProduct= 0x0124 - -MessageContent="5553424312345678000000000000061e000000000000000000000000000000" -MessageContent2="5553424312345679000000000000061b000000020000000000000000000000" -MessageContent3="55534243123456702000000080000c85010101180101010101000000000000" - -NeedResponse=1 - - -####################################################### -# Visiontek 82GH 3G - -DefaultVendor= 0x230d -DefaultProduct=0x0007 - -TargetClass=0xff - -Configuration=3 - - -####################################################### -# HP LaserJet Professional P1102 - -DefaultVendor= 0x03f0 -DefaultProduct=0x002a - -TargetClass=0x07 - -MessageContent="555342431234567800000000000006d0000000000000000000000000000000" - -NeedResponse=1 - - -####################################################### -# Mobile Action ("Smart Cable") -# -# No success check, device will vanish completely - -DefaultVendor= 0x0df7 -DefaultProduct=0x0800 - -TargetClass=0xff - -MobileActionMode=1 - -NoDriverLoading=1 - - -######################################################## -# Vodafone/Huawei K3770 -# -# Contributor: Alex Chiang - -DefaultVendor= 0x12d1 -DefaultProduct=0x14d1 - -TargetVendor= 0x12d1 -TargetProduct= 0x14c9 - -MessageContent="55534243123456780000000000000011062000000100000000000000000000" - - -######################################################## -# Vodafone/Huawei K3771 -# -# Contributor: Ben Knight - -DefaultVendor= 0x12d1 -DefaultProduct=0x14c4 - -TargetVendor= 0x12d1 -TargetProduct= 0x14ca - -MessageContent="55534243123456780000000000000011062000000100000000000000000000" - - -######################################################## -# Option iCon 461 -# -# Contributor: Samit Basu - -DefaultVendor= 0x0af0 -DefaultProduct= 0x7a05 - -TargetClass= 0xff - -MessageContent="55534243123456780000000000000601000000000000000000000000000000" - -######################################################## -# ZTE AX226 WiMax -# -# may be supported by the Beceem driver - -DefaultVendor= 0x19d2 -DefaultProduct=0xbccd - -TargetVendor= 0x19d2 -TargetProduct= 0x0172 - -MessageContent="555342431234567824000000800006bc626563240000000000000000000000" - -NoDriverLoading=1 - - -######################################################## -# Huawei EC156 - -DefaultVendor= 0x12d1 -DefaultProduct=0x1505 - -TargetVendor= 0x12d1 -TargetProduct= 0x140b - -MessageContent="55534243123456780000000000000011062000000100000000000000000000" - - -######################################################## -# Longcheer SU9800 - -DefaultVendor= 0x1c9e -DefaultProduct=0x9800 - -TargetClass=0xff - -MessageContent="55534243123456788000000080000606f50402527000000000000000000000" - - -######################################################## -# SpeedUp SU-8000U - -DefaultVendor= 0x2020 -DefaultProduct=0xf00e - -TargetVendor= 0x2020 -TargetProduct= 0x1005 - -MessageContent="5553424312345678000000000000061b000000020000000000000000000000" - - -######################################################## -# Cisco AM10 "Valet Connector" -# -# Contributor: David S. - -DefaultVendor= 0x1307 -DefaultProduct=0x1169 - -TargetVendor= 0x13b1 -TargetProduct= 0x0031 - -CiscoMode=1 - - -####################################################### diff --git a/release/src/router/usbmodeswitch/dispatcher.c b/release/src/router/usbmodeswitch/dispatcher.c new file mode 100644 index 0000000000..23b2671002 --- /dev/null +++ b/release/src/router/usbmodeswitch/dispatcher.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011-2013 Josua Dietze, usb_modeswitch version 1.2.6 + * Contains code under + * Copyright (c) 2010 Wojciech A. Koszek + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ +#include +#include +#include +#include + +#include + +/* RAW is defined to the complete Tcl code in that file: */ +#include "usb_modeswitch.string" + +#define MAX_ARGSIZE 1024 + +int main(int argc, char **argv) +{ + int i, retval; + char arg[MAX_ARGSIZE]; + char arglist[MAX_ARGSIZE]; + + Jim_Interp *interp; + + interp = NULL; + arglist[0] = '\0'; + + for (i=1; i MAX_ARGSIZE-4) { + printf("Argument #%d extends maximum size, skip it\n", i); + continue; + } + if ( (strlen(arglist) + strlen(argv[i])) > MAX_ARGSIZE-4) { + printf("Argument #%d would extend maximum list size, skip it\n", i); + continue; + } + sprintf(arg,"{%s} ",argv[i]); + strncat(arglist,arg,MAX_ARGSIZE-1); + } + + char code[sizeof(RAW) + sizeof(arglist) + 28]; + sprintf(code, "set argv {%s}\nset argc %d\n", arglist, argc-1); + strncat(code, RAW, sizeof(RAW)); + + /* Create Jim instance */ + interp = Jim_CreateInterp(); + assert(interp != NULL && "Could not create interpreter!"); + + /* Register base commands, actually implementing Tcl */ + Jim_RegisterCoreCommands(interp); + + /* Initialize any static extensions */ + Jim_InitStaticExtensions(interp); + + /* Evaluate the script that's now in "code" */ + retval = Jim_Eval(interp, code); + if (retval < 0) + printf("Evaluation returned error %d\n", retval); + + /* Free the interpreter */ + Jim_FreeInterp(interp); + return (EXIT_SUCCESS); +} diff --git a/release/src/router/usbmodeswitch/jim/AUTHORS b/release/src/router/usbmodeswitch/jim/AUTHORS new file mode 100644 index 0000000000..73413a51f5 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/AUTHORS @@ -0,0 +1,41 @@ +Salvatore Sanfilippo + +with the help (patches, bug reports, ideas, extensions) of: + +Pat Thoyts +Clemens Hintze + +See also the ChangeLog and README files for other credits. + +DESIGN CREDITS: + +some of the idea inside Jim are the fruit of long discussions +inside the Tclers chat room. The feedback of the Tcl +comunity in general, and of the members of the Tcl Core Team, was +very important to avoid mistakes: I used the great experience of +this people as a test for some of the ideas I put into Jim. +Bad ideas tend to be demolished in no time by good engineers. + +Also the following ideas are due to the following authors: + +- Jim locals were originally proposed by Miguel Sofer, I (SS) added + the feature that make they similar to lexical scoped closures + using capturing of the local variables value if no explicit + intialization is provided. + +- The [lmap] command is my (SS) design, but I incorporated inside the + command an interesting idea of Donal K. Fellows that proposed that + the [continue] command may be used to skip the accumulation of the + current-iteartion result, providing in one command the power of + [map] and [filter] together. + + +ChangeLog committers to be identified. Tentative list: + +antirez - Salvatore Sanfilippo +patthoyts - Pat Thoyts +oharboe - Øyvind Harboe - soyvind.harboe@zylin.com +Andrew Lunn +Duane Ellis +Uwe Klein +Clemens Hintze ml-jim@qiao.in-berlin.de aka chi diff --git a/release/src/router/usbmodeswitch/jim/LICENSE b/release/src/router/usbmodeswitch/jim/LICENSE new file mode 100644 index 0000000000..d5b9ecb6b0 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/LICENSE @@ -0,0 +1,45 @@ +Unless explicitly stated, all files within Jim repository are released +under following license: + +/* Jim - A small embeddable Tcl interpreter + * + * Copyright 2005 Salvatore Sanfilippo + * Copyright 2005 Clemens Hintze + * Copyright 2005 patthoyts - Pat Thoyts + * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com + * Copyright 2008 Andrew Lunn + * Copyright 2008 Duane Ellis + * Copyright 2008 Uwe Klein + * Copyright 2008 Steve Bennett + * Copyright 2009 Nico Coesel + * Copyright 2009 Zachary T Welch zw@superlucidity.net + * Copyright 2009 David Brownell + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + */ diff --git a/release/src/router/usbmodeswitch/jim/Makefile.in b/release/src/router/usbmodeswitch/jim/Makefile.in new file mode 100644 index 0000000000..0191c83fc9 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/Makefile.in @@ -0,0 +1,198 @@ +# Tools +CC ?= @CCACHE@ @CC@ +CXX = @CCACHE@ @CXX@ +RANLIB = @RANLIB@ +AR = @AR@ +STRIP = @STRIP@ + +# Configuration + +SH_CFLAGS ?= @SH_CFLAGS@ +SH_LDFLAGS ?= @SH_LDFLAGS@ +SHOBJ_CFLAGS ?= @SHOBJ_CFLAGS@ +SHOBJ_LDFLAGS ?= @SHOBJ_LDFLAGS@ +CFLAGS = @CFLAGS@ +CXXFLAGS = @CXXFLAGS@ +LDFLAGS = @LDFLAGS@ +LDLIBS += @LDLIBS@ +exec_prefix ?= @exec_prefix@ +prefix ?= @prefix@ + +CC += -D_GNU_SOURCE -Wall $(OPTIM) -I. +CXX += -D_GNU_SOURCE -Wall $(OPTIM) -I. +@if srcdir != . +CFLAGS += -I@srcdir@ +CXXFLAGS += -I@srcdir@ +VPATH := @srcdir@ +@endif + +@if JIM_STATICLIB +LIBJIM := libjim.a +@else +LIBJIM := libjim.so +SH_LIBJIM := $(LIBJIM) +CC += $(SH_CFLAGS) +CXX += $(SH_CFLAGS) +DEF_LD_PATH := @LD_LIBRARY_PATH@=`pwd` +@endif + +@if HAVE_CXX_EXTENSIONS +JIMSH_CC := $(CXX) $(CXXFLAGS) +@else +JIMSH_CC := $(CC) $(CFLAGS) +@endif + +OBJS := _load-static-exts.o jim-subcmd.o jim-interactive.o jim-format.o jim.o utf8.o jimregexp.o \ + @EXTRA_OBJS@ @C_EXT_OBJS@ @TCL_EXT_OBJS@ + +JIMSH := jimsh@EXEEXT@ + +all: $(JIMSH) @C_EXT_SHOBJS@ + +# Create C extensions from pure Tcl extensions +.SUFFIXES: .tcl +.tcl.o: + @tclsh@ @srcdir@/make-c-ext.tcl $< >_$*.c || ( rm _$*.c; exit 1) + $(CC) $(CFLAGS) -c -o $@ _$*.c || ( rm _$*.c; exit 1) + @rm -f _$*.c + +docs: Tcl.html + +$(JIMSH): $(LIBJIM) jimsh.o initjimsh.o + $(JIMSH_CC) @SH_LINKFLAGS@ $(LDFLAGS) -o $@ jimsh.o initjimsh.o $(LIBJIM) $(LDLIBS) + +@if JIM_INSTALL +install: all docs @TCL_EXTS@ install-exec + mkdir -p $(DESTDIR)$(prefix)/lib/jim + cp $(LIBJIM) $(DESTDIR)$(prefix)/lib + cp @srcdir@/README.extensions @C_EXT_SHOBJS@ @TCL_EXTS@ $(DESTDIR)$(prefix)/lib/jim + mkdir -p $(DESTDIR)$(prefix)/include + cp @srcdir@/jim.h @srcdir@/jim-eventloop.h @srcdir@/jim-nvp.h @srcdir@/jim-signal.h \ + @srcdir@/jim-subcmd.h @srcdir@/jim-win32compat.h $(DESTDIR)$(prefix)/include + cp jim-config.h $(DESTDIR)$(prefix)/include + mkdir -p $(DESTDIR)$(prefix)/doc/jim + cp Tcl.html $(DESTDIR)$(prefix)/doc/jim + +install-exec: all + mkdir -p $(DESTDIR)$(prefix)/bin + cp $(JIMSH) $(DESTDIR)$(prefix)/bin + +uninstall: + rm -f $(DESTDIR)$(prefix)/bin/$(JIMSH) + rm -f $(DESTDIR)$(prefix)/lib/$(LIBJIM) + for i in README.extensions @C_EXT_SHOBJS@ @TCL_EXTS@; do rm -f $(DESTDIR)$(prefix)/lib/jim/$$i; done + rm -f $(DESTDIR)$(prefix)/include/jim*.h + rm -f $(DESTDIR)$(prefix)/doc/jim/Tcl.html +@else +install install-exec uninstall: +@endif + +test: $(JIMSH) + $(DEF_LD_PATH) $(MAKE) -C @srcdir@/tests jimsh=`pwd`/jimsh + +$(OBJS): Makefile + +@if JIM_UTF8 +# Generate the unicode case mapping +utf8.o: _unicode_mapping.c + +_unicode_mapping.c: @srcdir@/UnicodeData.txt @srcdir@/parse-unidata.tcl + @tclsh@ @srcdir@/parse-unidata.tcl @srcdir@/UnicodeData.txt >$@ || ( rm $@; exit 1) +@endif + +_load-static-exts.c: @srcdir@/make-load-static-exts.tcl Makefile + @tclsh@ @srcdir@/make-load-static-exts.tcl @STATIC_EXTS@ >$@ || ( rm $@; exit 1) + +@if JIM_STATICLIB +$(LIBJIM): $(OBJS) + $(AR) cr $@ $(OBJS) + $(RANLIB) $@ +@else +$(LIBJIM): $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) $(SH_LDFLAGS) -o $@ $(OBJS) $(LDLIBS) +@endif + +# Note that $> $^ is for compatibility with both GNU make and BSD make +readdir.so: jim-readdir.c + $(CC) $(CFLAGS) $(SHOBJ_CFLAGS) -c -o jim-readdir.o $> $^ + $(CC) $(CFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-readdir.o $(SH_LIBJIM) + +array.so: jim-array.c + $(CC) $(CFLAGS) $(SHOBJ_CFLAGS) -c -o jim-array.o $> $^ + $(CC) $(CFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-array.o $(SH_LIBJIM) + +clock.so: jim-clock.c + $(CC) $(CFLAGS) $(SHOBJ_CFLAGS) -c -o jim-clock.o $> $^ + $(CC) $(CFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-clock.o $(SH_LIBJIM) + +file.so: jim-file.c + $(CC) $(CFLAGS) $(SHOBJ_CFLAGS) -c -o jim-file.o $> $^ + $(CC) $(CFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-file.o $(SH_LIBJIM) + +posix.so: jim-posix.c + $(CC) $(CFLAGS) $(SHOBJ_CFLAGS) -c -o jim-posix.o $> $^ + $(CC) $(CFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-posix.o $(SH_LIBJIM) + +regexp.so: jim-regexp.c + $(CC) $(CFLAGS) $(SHOBJ_CFLAGS) -c -o jim-regexp.o $> $^ + $(CC) $(CFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-regexp.o $(SH_LIBJIM) + +syslog.so: jim-syslog.c + $(CC) $(CFLAGS) $(SHOBJ_CFLAGS) -c -o jim-syslog.o $> $^ + $(CC) $(CFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-syslog.o $(SH_LIBJIM) + +readline.so: jim-readline.c + $(CC) $(CFLAGS) $(SHOBJ_CFLAGS) -c -o jim-readline.o $> $^ + $(CC) $(CFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-readline.o $(SH_LIBJIM) @LDLIBS_readline@ + +pack.so: jim-pack.c + $(CC) $(CFLAGS) $(SHOBJ_CFLAGS) -c -o jim-pack.o $> $^ + $(CC) $(CFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-pack.o $(SH_LIBJIM) @LDLIBS_pack@ + +sqlite.so: jim-sqlite.c + $(CC) $(CFLAGS) $(SHOBJ_CFLAGS) -c -o jim-sqlite.o $> $^ + $(CC) $(CFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-sqlite.o $(SH_LIBJIM) @LDLIBS_sqlite@ + +sqlite3.so: jim-sqlite3.c + $(CC) $(CFLAGS) $(SHOBJ_CFLAGS) -c -o jim-sqlite3.o $> $^ + $(CC) $(CFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-sqlite3.o $(SH_LIBJIM) @LDLIBS_sqlite3@ + +win32.so: jim-win32.c + $(CC) $(CFLAGS) $(SHOBJ_CFLAGS) -c -o jim-win32.o $> $^ + $(CC) $(CFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-win32.o $(SH_LIBJIM) @LDLIBS_win32@ + +mk.so: jim-mk.cpp + $(CXX) $(CXXFLAGS) $(SHOBJ_CFLAGS) -c -o jim-mk.o $> $^ + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-mk.o $(SH_LIBJIM) @LDLIBS_mk@ + +sdl.so: jim-sdl.c + $(CC) $(CFLAGS) $(SHOBJ_CFLAGS) -c -o jim-sdl.o $> $^ + $(CC) $(CFLAGS) $(LDFLAGS) $(SHOBJ_LDFLAGS) -o $@ jim-sdl.o $(SH_LIBJIM) @LDLIBS_sdl@ + +Tcl.html: jim_tcl.txt + @tclsh@ @srcdir@/make-index $> $^ | asciidoc -o $@ -d manpage - || cp @srcdir@/Tcl_shipped.html Tcl.html + +clean: + rm -f *.o *.so lib*.a $(JIMSH) Tcl.html _*.c + +distclean: clean + rm -f jimautoconf.h jim-config.h Makefile config.log autosetup/jimsh0.c autosetup/jimsh0@EXEEXT@ + +ship: Tcl.html + cp $< Tcl_shipped.html + +# automake compatibility. do nothing for all these targets +EMPTY_AUTOMAKE_TARGETS := dvi pdf ps info html tags ctags mostlyclean maintainer-clean check installcheck installdirs \ + install-pdf install-ps install-info install-html -install-dvi uninstall install-data +.PHONY: $(EMPTY_AUTOMAKE_TARGETS) +$(EMPTY_AUTOMAKE_TARGETS): + +# automake compatibility - install sources from the current dir to $(distdir) +distdir_full := $(shell cd $(distdir); pwd) +distdir: + cd "@srcdir@"; git ls-files | cpio -pdmu $(distdir_full) + +reconfig: + CC='@CC@' @AUTOREMAKE@ + +lib: $(LIBJIM) diff --git a/release/src/router/usbmodeswitch/jim/README b/release/src/router/usbmodeswitch/jim/README new file mode 100644 index 0000000000..8acacc4139 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/README @@ -0,0 +1,235 @@ +The Jim Interpreter + +A small-footprint implementation of the Tcl programming language. + +-------------------------------------------------------------------------------- +WHAT IS JIM? +-------------------------------------------------------------------------------- + +Jim is a small footprint implementation of the Tcl programming language +written from scratch. Currently Jim Tcl is very feature complete with +an extensive test suite (see the tests directory). +There are some Tcl commands and features which are not implemented +(and likely never will be), including namespaces, traces and Tk. However +Jim Tcl offers a number of both Tcl8.5 and Tcl8.6 features ({*}, dict, lassign, +tailcall and optional UTF-8 support) and some unique features. +These unique features include [lambda] with garbage collection, a general GC/references +system, arrays as syntax sugar for [dict]tionaries, object-based I/O and more. + +Other common features of the Tcl programming language are present, like +the "everything is a string" behaviour, implemented internally as +dual ported objects to ensure that the execution time does not reflect +the semantic of the language :) + +-------------------------------------------------------------------------------- +WHEN JIM CAN BE USEFUL? +-------------------------------------------------------------------------------- + +1) If you are writing an application, and want to make it scriptable, with +Jim you have a way to do it that does not require to link your application +with a big system. You can include the Jim source directly in your project +and use the Jim API to write the glue code that makes your application +scriptable in Jim, with the following advantages: + +- Jim is not the next "little language", but it's a Tcl implementation. + You can reuse your knowledge if you already Tcl skills, or enjoy + the availability of documentation, books, web resources, ... + (for example check my online Tcl book at http://www.invece.org/tclwise) + +- Jim is simple, 14k lines of core code. If you want to adapt it you can hack + the source code to meet the needs of your application. It makes you + able to have scripting for default, and avoid external dependences. + + Having scripting support *inside*, and in a way that a given version + of your program always gets shipped a given version of Jim, you can + write part of your application in Jim itself. Like it happens for + Emacs/Elisp, or Gimp/Scheme, both this applications have the interpreter + inside. + +- Jim is Tcl, and Tcl looks like a configuration file if you want. So + if you use Jim you have also a flexible syntax for your config file. + This is a valid Tcl script: + + set MyFeature on + ifssl { + set SslPort 45000 + use compression + } + + It looks like a configuration file, but if you implement the [ifssl] + and [use] commands, it's a valid Tcl script. + +- Tcl scales with the user. Not all know it, but Tcl is so powerful that + you can reprogram the language in itself. Jim support this features + of the Tcl programming language. You can write new control structures, + use the flexible data types it offers (Lists are a central data structure, + with Dictionaries that are also lists). Still Tcl is simpler for the + casual programmer, especially if compared to other languages offering + small footprint implementations (like Scheme and FORTH). + +- Because of the Tcl semantic (pass by value, everything is a command + since there are no reserved words), there is a nice API to glue + your application with Jim. See under the Jim Tcl manual for more detail. + +- Jim is supported. If you need commercial software, contact the original author + at 'antirez@gmail.com' or the current maintainer at 'steveb@workware.net.au'. + +2) The other "field" where Jim can be useful is obviously embedded systems. + +3) We are working to make Jim as feature-complete as possible, thanks to + dynamically loaded extensions it may stay as little as it is today + but able to do interesting things for you. So it's not excluded that + in the future Jim will be an option as general purpose language. + But don't mind, for this there is already the mainstream Tcl + implementation ;). + +-------------------------------------------------------------------------------- +HOW BIG IS IT? +-------------------------------------------------------------------------------- + +Jim with the default extensions configured and compiled with -Os is about 130k. +Without any extensions, it is about 85k. + +-------------------------------------------------------------------------------- +HOW FAST IS IT? +-------------------------------------------------------------------------------- + +Jim is in most code faster than Tcl7.6p2 (latest 7.x version), +and slower than Tcl 8.4.x. You can expect pretty decent performance +for such a little interpreter. + +If you want a more precise measure, there is 'bench.tcl' inside this +distribution that will run both under Jim and Tcl, so just execute +it with both the interpreters and see what you get :) + +-------------------------------------------------------------------------------- +HOW TO COMPILE +-------------------------------------------------------------------------------- + +Jim was tested under Linux, FreeBSD, MacosX, eCos, QNX, Windows XP (mingw, MVC). + +To compile jim itself try: + + ./configure + make + +-------------------------------------------------------------------------------- +EXTENSIONS +-------------------------------------------------------------------------------- + +Many optional extensions are included. Some are C extensions and others are pure Tcl. +Form more information, try: + + ./configure --help + +-------------------------------------------------------------------------------- +HOW TO EMBED JIM INTO APPLICATIONS +-------------------------------------------------------------------------------- + +See the "examples.api" directory + +-------------------------------------------------------------------------------- +HOW TO WRITE EXTENSIONS FOR JIM +-------------------------------------------------------------------------------- + +See the extensions shipped with Jim, jim-readline.c, jim-clock.c, glob.tcl and oo.tcl + +-------------------------------------------------------------------------------- +COPYRIGHT and LICENSE +-------------------------------------------------------------------------------- + +Unless explicitly stated, all files within Jim repository are released +under following license: + +/* Jim - A small embeddable Tcl interpreter + * + * Copyright 2005 Salvatore Sanfilippo + * Copyright 2005 Clemens Hintze + * Copyright 2005 patthoyts - Pat Thoyts + * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com + * Copyright 2008 Andrew Lunn + * Copyright 2008 Duane Ellis + * Copyright 2008 Uwe Klein + * Copyright 2008 Steve Bennett + * Copyright 2009 Nico Coesel + * Copyright 2009 Zachary T Welch zw@superlucidity.net + * Copyright 2009 David Brownell + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + */ +-------------------------------------------------------------------------------- +HISTORY +-------------------------------------------------------------------------------- + +"first Jim goal: to vent my need to hack on Tcl." + +And actually this is exactly why I started Jim, in the first days +of Jenuary 2005. After a month of hacking Jim was able to run +simple scripts, now, after two months it started to be clear to +me that it was not just the next toy to throw away but something +that may evolve into a real interpreter. In the same time +Pat Thoyts and Clemens Hintze started to contribute code, so that +the development of new core commands was faster, and also more +people hacking on the same code had as result fixes in the API, +C macros, and so on. + +Currently we are at the point that the core interpreter is almost finished +and it is entering the Beta stage. There is to add some other core command, +to do a code review to ensure quality of all the parts and to write +documentation. + +We already started to work on extensions like OOP, event loop, +I/O, networking, regexp. Some extensions are already ready for +prime time, like the Sqlite extension and the ANSI I/O. + +------------------------------------------------------------------------------ +Thanks to... +------------------------------------------------------------------------------ + +- First of all, thanks to every guy that are listed in the AUTHORS file, + that directly helped with code and ideas. Also check the ChangeLog + file for additional credits about patches or bug reports. +- Elisa Manara that helped me to select this ill conceived name for + an interpreter. +- Many people on the Tclers Chat that helped me to explore issues + about the use and the implementation of the Tcl programming language. +- David Welton for the tech info sharing and our chats about + programming languages design and the ability of software to "scale down". +- Martin S. Weber for the great help with Solaris issues, debugging of + problems with [load] on this arch, 64bit tests. +- The authors of "valgrind", for this wonderful tool, that helped me a + lot to fix bugs in minutes instead of hours. + + +---- +Enjoy! +Salvatore Sanfilippo +10 Mar 2005 + + diff --git a/release/src/router/usbmodeswitch/jim/README.usb_modeswitch b/release/src/router/usbmodeswitch/jim/README.usb_modeswitch new file mode 100644 index 0000000000..7f3ae4175c --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/README.usb_modeswitch @@ -0,0 +1,6 @@ +The content of this folder is customized for shipping with usb_modeswitch; +for the original source see: + +http://repo.or.cz/w/jimtcl.git + +This is version 0.72 of jimtcl diff --git a/release/src/router/usbmodeswitch/jim/auto.def b/release/src/router/usbmodeswitch/jim/auto.def new file mode 100644 index 0000000000..2cdb85cc46 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/auto.def @@ -0,0 +1,436 @@ +# vim:se syn=tcl: +# + +# Note: modules which support options *must* be included before 'options' +use cc cc-shared + +options { + utf8 => "include support for utf8-encoded strings" + lineedit=1 => "disable line editing" + references=1 => "disable support for references" + math => "include support for math functions" + ipv6 => "include ipv6 support in the aio extension" + maintainer => {enable the [debug] command and JimPanic} + full => "Enable some optional features: ipv6, math, utf8, binary, oo, tree" + with-jim-shared shared => "build a shared library instead of a static library" + jim-regexp => "use the built-in (Tcl-compatible) regexp, even if POSIX regex is available" + with-jim-ext: {with-ext:"ext1 ext2 ..."} => { + Specify additional jim extensions to include. + These are enabled by default: + + aio - ANSI I/O, including open and socket + eventloop - after, vwait, update + array - Tcl-compatible array command + clock - Tcl-compatible clock command + exec - Tcl-compatible exec command + file - Tcl-compatible file command + glob - Tcl-compatible glob command + readdir - Required for glob + package - Package management with the package command + load - Load binary extensions at runtime with load or package + posix - Posix APIs including os.fork, os.wait, pid + regexp - Tcl-compatible regexp, regsub commands + signal - Signal handling + stdlib - Built-in commands including lassign, lambda, alias + syslog - System logging with syslog + tclcompat - Tcl compatible read, gets, puts, parray, case, ... + + These are disabled by default: + + nvp - Name-value pairs C-only API + oo - Jim OO extension + tree - OO tree structure, similar to tcllib ::struct::tree + binary - Tcl-compatible 'binary' command + readline - Interface to libreadline + rlprompt - Tcl wrapper around the readline extension + mk - Interface to Metakit + sqlite - Interface to sqlite + sqlite3 - Interface to sqlite3 + win32 - Interface to win32 + } + with-out-jim-ext: {without-ext:"default|ext1 ext2 ..."} => { + Specify jim extensions to exclude. + If 'default' is given, the default extensions will not be added. + } + with-jim-extmod: {with-mod:"ext1 ext2 ..."} => { + Specify jim extensions to build as separate modules (either C or Tcl). + Note that not all extensions can be built as loadable modules. + } + # To help out openocd with automake + install-jim=1 +} + +cc-check-types "long long" + +cc-check-includes sys/socket.h netinet/in.h arpa/inet.h netdb.h +cc-check-includes sys/un.h dlfcn.h unistd.h crt_externs.h + +define LDLIBS "" + +# Haiku needs -lnetwork, Solaris needs -lnsl +if {[cc-check-function-in-lib inet_ntop {nsl network}]} { + # This does nothing if no libs are needed + cc-with [list -libs [get-define lib_inet_ntop]] + define-append LDLIBS [get-define lib_inet_ntop] +} +# Solaris needs -lsocket, Windows needs -lwsock32 +if {[cc-check-function-in-lib socket socket]} { + define-append LDLIBS [get-define lib_socket] +} + +cc-check-functions ualarm lstat fork vfork system select +cc-check-functions backtrace geteuid mkstemp realpath strptime gettimeofday +cc-check-functions regcomp waitpid sigaction sys_signame sys_siglist +cc-check-functions syslog opendir readlink sleep usleep pipe getaddrinfo utimes +if {[cc-check-functions sysinfo]} { + cc-with {-includes sys/sysinfo.h} { + cc-check-members "struct sysinfo.uptime" + } +} + +define TCL_LIBRARY [get-define prefix]/lib/jim + +lassign [split [get-define host] -] host_cpu host_vendor host_os +# Scrub revision from the host_os +regsub -all {[0-9.]} $host_os {} host_os + +switch -glob -- $host_os { + mingw* { + # We provide our own implementation of dlopen for mingw32 + define-feature dlopen-compat + define-feature winconsole + define TCL_PLATFORM_OS $host_os + define TCL_PLATFORM_PLATFORM windows + define TCL_PLATFORM_PATH_SEPARATOR {;} + } + default { + # Note that cygwin is considered a unix platform + define TCL_PLATFORM_OS $host_os + define TCL_PLATFORM_PLATFORM unix + define TCL_PLATFORM_PATH_SEPARATOR : + } +} + +# Find some tools +cc-check-tools ar ranlib strip +define tclsh [info nameofexecutable] + +if {![cc-check-functions _NSGetEnviron]} { + msg-checking "Checking environ declared in unistd.h..." + if {[cctest -cflags -D_GNU_SOURCE -includes unistd.h -code {char **ep = environ;}]} { + define NO_ENVIRON_EXTERN + msg-result "yes" + } else { + msg-result "no" + } +} + +# Windows has a mkdir with no permission arg +cc-check-includes sys/types.h sys/stat.h +msg-checking "Checking for mkdir with one arg..." +if {[cctest -includes {sys/types.h sys/stat.h} -code {mkdir("/dummy");}]} { + define HAVE_MKDIR_ONE_ARG + msg-result yes +} else { + msg-result no +} + +# autosetup can't handle C++ libraries +proc check-metakit {} { + set found 0 + cc-with {-lang c++} { + msg-checking "Checking for Metakit..." + if {[cctest -includes mk4.h -libs -lmk4 -code {c4_Storage dummy();}]} { + msg-result ok + define lib_mk -lmk4 + incr found + } else { + msg-result "not found" + } + } + return $found +} + +set extra_objs {} +set jimregexp 0 + +if {[opt-bool utf8 full]} { + msg-result "Enabling UTF-8" + define JIM_UTF8 + incr jimregexp +} else { + define JIM_UTF8 0 +} +if {[opt-bool maintainer]} { + msg-result "Enabling maintainer settings" + define JIM_MAINTAINER +} +if {[opt-bool math full]} { + msg-result "Enabling math functions" + define JIM_MATH_FUNCTIONS + cc-check-function-in-lib sin m + define-append LDLIBS [get-define lib_sin] +} +if {[opt-bool ipv6 full]} { + msg-result "Enabling IPv6" + define JIM_IPV6 +} +if {[opt-bool lineedit full]} { + if {([cc-check-includes termios.h] && [cc-check-functions isatty]) || [have-feature winconsole]} { + msg-result "Enabling line editing" + define USE_LINENOISE + lappend extra_objs linenoise.o + } +} +if {[opt-bool references]} { + msg-result "Enabling references" + define JIM_REFERENCES +} +if {[opt-bool shared with-jim-shared]} { + msg-result "Building shared library" +} else { + msg-result "Building static library" + define JIM_STATICLIB +} +define JIM_INSTALL [opt-bool install-jim] + +# Note: Extension handling is mapped directly from the configure.ac +# implementation + +set without [join [opt-val {without-ext with-out-jim-ext}]] +set withext [join [opt-val {with-ext with-jim-ext}]] +set withmod [join [opt-val {with-mod with-jim-extmod}]] + +# Tcl extensions +set ext_tcl "stdlib glob tclcompat tree rlprompt oo binary" +# Native extensions +set ext_c "load package readdir array clock exec file posix regexp signal aio eventloop pack syslog nvp readline mk sqlite sqlite3 win32 sdl" + +# C++ extensions +set ext_cxx "mk" + +# Tcl extensions which can be modules +set ext_tcl_mod "glob tree rlprompt oo binary" +# Native extensions which can be modules +set ext_c_mod "readdir array clock file posix regexp syslog readline pack mk sqlite sqlite3 win32 sdl" + +# All extensions +set ext_all [concat $ext_c $ext_tcl] + +# Default static extensions +set ext_default "stdlib load package readdir glob array clock exec file posix regexp signal tclcompat aio eventloop syslog" + +if {[opt-bool full]} { + lappend ext_default tree binary +} + +if {$without eq "default"} { + set ext_default stdlib + set without {} +} + +# Check valid extension names +foreach i [concat $withext $without $withmod] { + if {$i ni $ext_all} { + user-error "Unknown extension: $i" + } +} + +# needs_xxx="expression" means that the expr must eval to 1 to select the extension +# dep_xxx="yyy zzz" means that if xxx is selected, so is yyy and zzz +set dep(glob) readdir +set dep(rlprompt) readline +set dep(tree) oo +set dep(binary) pack + +set needs(exec) {expr {([have-feature vfork] && [have-feature waitpid]) || [have-feature system]}} +set needs(load) {expr {[cc-check-function-in-lib dlopen dl] || [have-feature dlopen-compat]}} +set libdep(load) lib_dlopen +set needs(posix) {have-feature waitpid} +set needs(readdir) {have-feature opendir} +set needs(readline) {cc-check-function-in-lib readline readline} +set libdep(readline) lib_readline +set needs(signal) {expr {[have-feature sigaction] && [have-feature vfork]}} +set needs(mk) {check-metakit} +set libdep(mk) lib_mk +set needs(sqlite) {cc-check-function-in-lib sqlite_open sqlite} +set libdep(sqlite) lib_sqlite_open +set needs(sqlite3) {cc-check-function-in-lib sqlite3_open sqlite3} +set libdep(sqlite3) lib_sqlite3_open +set needs(syslog) {have-feature syslog} +set needs(win32) {have-feature windows} +set needs(sdl) {expr {[cc-check-function-in-lib SDL_SetVideoMode SDL] && [cc-check-function-in-lib rectangleRGBA SDL_gfx]}} +set libdep(sdl) {lib_SDL_SetVideoMode lib_rectangleRGBA} + +# First handle dependencies. If an extension is enabled, also enable its dependency +foreach i [concat $ext_default $withext] { + if {$i in $without} { + continue + } + if {[info exists dep($i)]} { + lappend withext {*}$dep($i) + } +} + +foreach i $withmod { + if {[info exists dep($i)]} { + # Theoretically, a mod could depend upon something which must be static + # If already configured static, don't make it a module + foreach d $dep($i) { + if {$d ni $withext} { + lappend withmod $d + } + } + } +} + +# Now that we know what the platform supports: + +# For all known extensions: +# - If it is disabled, remove it +# - Otherwise, check to see if it's pre-requisites are met +# - If yes, add it if it is enabled or is a default +# - If no, error if it is enabled, or do nothing otherwise +# - Modules may be either C or Tcl + +set extmodtcl {} +set extmod {} +set ext {} + +foreach i [lsort $ext_all] { + # First discard the extension if disabled or not enabled + if {$i in $without} { + msg-result "Extension $i...disabled" + continue + } + if {$i ni [concat $withext $withmod $ext_default]} { + msg-result "Extension $i...not enabled" + continue + } + + # Check dependencies + set met 1 + if {[info exists needs($i)]} { + set met [eval $needs($i)] + } + + define LDLIBS_$i "" + + msg-checking "Extension $i..." + + # Selected as a module? + if {$i in $withmod} { + if {$i in $ext_tcl_mod} { + # Easy, a Tcl module + msg-result "tcl" + lappend extmodtcl $i + continue + } + if {$i ni $ext_c_mod} { + user-error "not a module" + } + if {!$met} { + user-error "dependencies not met" + } + msg-result "module" + lappend extmod $i + if {[info exists libdep($i)]} { + foreach j $libdep($i) { + define-append LDLIBS_$i [get-define $j ""] + } + } + continue + } + + # Selected as a static extension? + if {$i in $withext} { + if {!$met} { + user-error "dependencies not met" + } + msg-result "enabled" + } elseif {$i in $ext_default} { + if {!$met} { + msg-result "disabled (dependencies)" + continue + } + msg-result "enabled (default)" + } else { + continue + } + + lappend ext $i + if {[info exists libdep($i)]} { + foreach j $libdep($i) { + define-append LDLIBS [get-define $j ""] + } + } +} + +if {[have-feature windows]} { + lappend extra_objs jim-win32compat.o + + if {$extmod ne "" && [get-define JIM_LIBTYPE] eq "static"} { + user-error "cygwin/mingw require --shared for dynamic modules" + } +} + +if {"regexp" in "$ext $extmod"} { + # No regcomp means we need to use the built-in version + if {![have-feature regcomp]} { + incr jimregexp + } +} + +if {$jimregexp || [opt-bool jim-regexp]} { + msg-result "Using built-in regexp" + define JIM_REGEXP + + # If the built-in regexp overrides the system regcomp, etc. + # jim must be built shared so that the correct symbols are found + if {"regexp" in $extmod && [get-define JIM_LIBTYPE] eq "static" && [have-feature regcomp]} { + user-error "Must use --shared with regexp module and built-in regexp" + } +} +if {"load" ni $ext} { + # If we don't have load, no need to support shared objects + define SH_LINKFLAGS "" +} + +msg-result "Jim static extensions: [lsort $ext]" +if {$extmodtcl ne ""} { + msg-result "Jim Tcl extensions: [lsort $extmodtcl]" +} +if {$extmod ne ""} { + msg-result "Jim dynamic extensions: [lsort $extmod]" +} + +# Separate out the static extensions into C and Tcl +set ext_static_c {} +set ext_static_tcl {} +foreach e $ext { + define jim_ext_$e + if {$e in $ext_tcl} { + lappend ext_static_tcl $e + } else { + lappend ext_static_c $e + } +} + +# If there are any static C++ extensions, jimsh must be linked using +# the C++ compiler +foreach e $ext_static_c { + if {$e in $ext_cxx} { + define HAVE_CXX_EXTENSIONS + } +} + +define STATIC_EXTS [concat $ext_static_c $ext_static_tcl] +define C_EXT_OBJS [prefix jim- [suffix .o $ext_static_c]] +define TCL_EXT_OBJS [suffix .o $ext_static_tcl] +define C_EXT_SHOBJS [suffix .so $extmod] +define TCL_EXTS [suffix .tcl $extmodtcl] +define EXTRA_OBJS $extra_objs + +make-config-header jim-config.h -auto {HAVE_LONG_LONG* JIM_UTF8} -none * +make-config-header jimautoconf.h -auto {jim_ext_* TCL_PLATFORM_* TCL_LIBRARY USE_* JIM_*} +make-template Makefile.in diff --git a/release/src/router/usbmodeswitch/jim/autosetup/LICENSE b/release/src/router/usbmodeswitch/jim/autosetup/LICENSE new file mode 100644 index 0000000000..4fe636c9d9 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/autosetup/LICENSE @@ -0,0 +1,35 @@ +Unless explicitly stated, all files which form part of autosetup +are released under the following license: + +--------------------------------------------------------------------- +autosetup - A build environment "autoconfigurator" + +Copyright (c) 2010-2011, WorkWare Systems + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE WORKWARE SYSTEMS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WORKWARE +SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation +are those of the authors and should not be interpreted as representing +official policies, either expressed or implied, of WorkWare Systems. diff --git a/release/src/router/usbmodeswitch/jim/autosetup/README.autosetup b/release/src/router/usbmodeswitch/jim/autosetup/README.autosetup new file mode 100644 index 0000000000..c7f69a846b --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/autosetup/README.autosetup @@ -0,0 +1 @@ +This is autosetup v0.6.3. See http://msteveb.github.com/autosetup/ diff --git a/release/src/router/usbmodeswitch/jim/autosetup/autosetup b/release/src/router/usbmodeswitch/jim/autosetup/autosetup new file mode 100755 index 0000000000..b1134c864e --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/autosetup/autosetup @@ -0,0 +1,1820 @@ +#!/bin/sh +# Copyright (c) 2006-2011 WorkWare Systems http://www.workware.net.au/ +# All rights reserved +# vim:se syntax=tcl: +# \ +dir=`dirname "$0"`; exec "`$dir/find-tclsh`" "$0" "$@" + +set autosetup(version) 0.6.3 + +# Can be set to 1 to debug early-init problems +set autosetup(debug) 0 + +################################################################## +# +# Main flow of control, option handling +# +proc main {argv} { + global autosetup define + + # There are 3 potential directories involved: + # 1. The directory containing autosetup (this script) + # 2. The directory containing auto.def + # 3. The current directory + + # From this we need to determine: + # a. The path to this script (and related support files) + # b. The path to auto.def + # c. The build directory, where output files are created + + # This is also complicated by the fact that autosetup may + # have been run via the configure wrapper ([getenv WRAPPER] is set) + + # Here are the rules. + # a. This script is $::argv0 + # => dir, prog, exe, libdir + # b. auto.def is in the directory containing the configure wrapper, + # otherwise it is in the current directory. + # => srcdir, autodef + # c. The build directory is the current directory + # => builddir, [pwd] + + # 'misc' is needed before we can do anything, so set a temporary libdir + # in case this is the development version + set autosetup(libdir) [file dirname $::argv0]/lib + use misc + + # (a) + set autosetup(dir) [realdir [file dirname [realpath $::argv0]]] + set autosetup(prog) [file join $autosetup(dir) [file tail $::argv0]] + set autosetup(exe) [getenv WRAPPER $autosetup(prog)] + if {$autosetup(installed)} { + set autosetup(libdir) $autosetup(dir) + } else { + set autosetup(libdir) [file join $autosetup(dir) lib] + } + autosetup_add_dep $autosetup(prog) + + # (b) + if {[getenv WRAPPER ""] eq ""} { + # Invoked directly + set autosetup(srcdir) [pwd] + } else { + # Invoked via the configure wrapper + set autosetup(srcdir) [file dirname $autosetup(exe)] + } + set autosetup(autodef) [relative-path $autosetup(srcdir)/auto.def] + + # (c) + set autosetup(builddir) [pwd] + + set autosetup(argv) $argv + set autosetup(cmdline) {} + set autosetup(options) {} + set autosetup(optionhelp) {} + set autosetup(showhelp) 0 + + # Parse options + use getopt + + array set ::useropts [getopt argv] + + #"=Core Options:" + options-add { + help:=local => "display help and options. Optionally specify a module name, such as --help=system" + version => "display the version of autosetup" + ref:=text manual:=text + reference:=text => "display the autosetup command reference. 'text', 'wiki', 'asciidoc' or 'markdown'" + debug => "display debugging output as autosetup runs" + install:=. => "install autosetup to the current or given directory (in the 'autosetup/' subdirectory)" + force init => "create an initial 'configure' script if none exists" + # Undocumented options + option-checking=1 + nopager + quiet + timing + conf: + } + + #parray ::useropts + if {[opt-bool version]} { + puts $autosetup(version) + exit 0 + } + + # autosetup --conf=alternate-auto.def + if {[opt-val conf] ne ""} { + set autosetup(autodef) [opt-val conf] + } + + # Debugging output (set this early) + incr autosetup(debug) [opt-bool debug] + incr autosetup(force) [opt-bool force] + incr autosetup(msg-quiet) [opt-bool quiet] + incr autosetup(msg-timing) [opt-bool timing] + + # If the local module exists, source it now to allow for + # project-local customisations + if {[file exists $autosetup(libdir)/local.tcl]} { + use local + } + + if {[opt-val help] ne ""} { + incr autosetup(showhelp) + use help + autosetup_help [opt-val help] + } + + if {[opt-val {manual ref reference}] ne ""} { + use help + autosetup_reference [opt-val {manual ref reference}] + } + + if {[opt-bool init]} { + use init + autosetup_init + } + + if {[opt-val install] ne ""} { + use install + autosetup_install [opt-val install] + } + + if {![file exists $autosetup(autodef)]} { + # Check for invalid option first + options {} + user-error "No auto.def found in $autosetup(srcdir)" + } + + # Parse extra arguments into autosetup(cmdline) + foreach arg $argv { + if {[regexp {([^=]*)=(.*)} $arg -> n v]} { + dict set autosetup(cmdline) $n $v + define $n $v + } else { + user-error "Unexpected parameter: $arg" + } + } + + autosetup_add_dep $autosetup(autodef) + + set cmd [file-normalize $autosetup(exe)] + foreach arg $autosetup(argv) { + append cmd " [quote-if-needed $arg]" + } + define AUTOREMAKE $cmd + + # Log how we were invoked + configlog "Invoked as: [getenv WRAPPER $::argv0] [quote-argv $autosetup(argv)]" + + source $autosetup(autodef) + + # Could warn here if options {} was not specified + + show-notices + + if {$autosetup(debug)} { + parray define + } + + exit 0 +} + +# @opt-bool option ... +# +# Check each of the named, boolean options and return 1 if any of them have +# been set by the user. +# +proc opt-bool {args} { + option-check-names {*}$args + opt_bool ::useropts {*}$args +} + +# @opt-val option-list ?default=""? +# +# Returns a list containing all the values given for the non-boolean options in 'option-list'. +# There will be one entry in the list for each option given by the user, including if the +# same option was used multiple times. +# If only a single value is required, use something like: +# +## lindex [opt-val $names] end +# +# If no options were set, $default is returned (exactly, not as a list). +# +proc opt-val {names {default ""}} { + option-check-names {*}$names + join [opt_val ::useropts $names $default] +} + +proc option-check-names {args} { + foreach o $args { + if {$o ni $::autosetup(options)} { + autosetup-error "Request for undeclared option --$o" + } + } +} + +# Parse the option definition in $opts and update +# ::useropts() and ::autosetup(optionhelp) appropriately +# +proc options-add {opts {header ""}} { + global useropts autosetup + + # First weed out comment lines + set realopts {} + foreach line [split $opts \n] { + if {![string match "#*" [string trimleft $line]]} { + append realopts $line \n + } + } + set opts $realopts + + for {set i 0} {$i < [llength $opts]} {incr i} { + set opt [lindex $opts $i] + if {[string match =* $opt]} { + # This is a special heading + lappend autosetup(optionhelp) $opt "" + set header {} + continue + } + + #puts "i=$i, opt=$opt" + regexp {^([^:=]*)(:)?(=)?(.*)$} $opt -> name colon equal value + if {$name in $autosetup(options)} { + autosetup-error "Option $name already specified" + } + + #puts "$opt => $name $colon $equal $value" + + # Find the corresponding value in the user options + # and set the default if necessary + if {[string match "-*" $opt]} { + # This is a documentation-only option, like "-C " + set opthelp $opt + } elseif {$colon eq ""} { + # Boolean option + lappend autosetup(options) $name + + if {![info exists useropts($name)]} { + set useropts($name) $value + } + if {$value eq "1"} { + set opthelp "--disable-$name" + } else { + set opthelp "--$name" + } + } else { + # String option. + lappend autosetup(options) $name + + if {$equal eq "="} { + if {[info exists useropts($name)]} { + # If the user specified the option with no value, the value will be "1" + # Replace with the default + if {$useropts($name) eq "1"} { + set useropts($name) $value + } + } + set opthelp "--$name?=$value?" + } else { + set opthelp "--$name=$value" + } + } + + # Now create the help for this option if appropriate + if {[lindex $opts $i+1] eq "=>"} { + set desc [lindex $opts $i+2] + #string match \n* $desc + if {$header ne ""} { + lappend autosetup(optionhelp) $header "" + set header "" + } + # A multi-line description + lappend autosetup(optionhelp) $opthelp $desc + incr i 2 + } + } +} + +# @module-options optionlist +# +# Like 'options', but used within a module. +proc module-options {opts} { + set header "" + if {$::autosetup(showhelp) > 1 && [llength $opts]} { + set header "Module Options:" + } + options-add $opts $header + + if {$::autosetup(showhelp)} { + # Ensure that the module isn't executed on --help + # We are running under eval or source, so use break + # to prevent further execution + #return -code break -level 2 + return -code break + } +} + +proc max {a b} { + expr {$a > $b ? $a : $b} +} + +proc options-wrap-desc {text length firstprefix nextprefix initial} { + set len $initial + set space $firstprefix + foreach word [split $text] { + set word [string trim $word] + if {$word == ""} { + continue + } + if {$len && [string length $space$word] + $len >= $length} { + puts "" + set len 0 + set space $nextprefix + } + incr len [string length $space$word] + puts -nonewline $space$word + set space " " + } + if {$len} { + puts "" + } +} + +proc options-show {} { + # Determine the max option width + set max 0 + foreach {opt desc} $::autosetup(optionhelp) { + if {[string match =* $opt] || [string match \n* $desc]} { + continue + } + set max [max $max [string length $opt]] + } + set indent [string repeat " " [expr $max+4]] + set cols [getenv COLUMNS 80] + catch { + lassign [exec stty size] rows cols + } + incr cols -1 + # Now output + foreach {opt desc} $::autosetup(optionhelp) { + if {[string match =* $opt]} { + puts [string range $opt 1 end] + continue + } + puts -nonewline " [format %-${max}s $opt]" + if {[string match \n* $desc]} { + puts $desc + } else { + options-wrap-desc [string trim $desc] $cols " " $indent [expr $max + 2] + } + } +} + +# @options options-spec +# +# Specifies configuration-time options which may be selected by the user +# and checked with opt-val and opt-bool. The format of options-spec follows. +# +# A boolean option is of the form: +# +## name[=0|1] => "Description of this boolean option" +# +# The default is name=0, meaning that the option is disabled by default. +# If name=1 is used to make the option enabled by default, the description should reflect +# that with text like "Disable support for ...". +# +# An argument option (one which takes a parameter) is of the form: +# +## name:[=]value => "Description of this option" +# +# If the name:value form is used, the value must be provided with the option (as --name=myvalue). +# If the name:=value form is used, the value is optional and the given value is used as the default +# if is not provided. +# +# Undocumented options are also supported by omitting the "=> description. +# These options are not displayed with --help and can be useful for internal options or as aliases. +# +# For example, --disable-lfs is an alias for --disable=largefile: +# +## lfs=1 largefile=1 => "Disable large file support" +# +proc options {optlist} { + # Allow options as a list or args + options-add $optlist "Local Options:" + + if {$::autosetup(showhelp)} { + options-show + exit 0 + } + + # Check for invalid options + if {[opt-bool option-checking]} { + foreach o [array names ::useropts] { + if {$o ni $::autosetup(options)} { + user-error "Unknown option --$o" + } + } + } +} + +proc config_guess {} { + if {[file-isexec $::autosetup(dir)/config.guess]} { + exec-with-stderr sh $::autosetup(dir)/config.guess + } else { + configlog "No config.guess, so using uname" + string tolower [exec uname -p]-unknown-[exec uname -s][exec uname -r] + } +} + +proc config_sub {alias} { + if {[file-isexec $::autosetup(dir)/config.sub]} { + exec-with-stderr sh $::autosetup(dir)/config.sub $alias + } else { + return $alias + } +} + +# @define name ?value=1? +# +# Defines the named variable to the given value. +# These (name, value) pairs represent the results of the configuration check +# and are available to be checked, modified and substituted. +# +proc define {name {value 1}} { + set ::define($name) $value + #dputs "$name <= $value" +} + +# @define-append name value ... +# +# Appends the given value(s) to the given 'defined' variable. +# If the variable is not defined or empty, it is set to $value. +# Otherwise the value is appended, separated by a space. +# Any extra values are similarly appended. +# If any value is already contained in the variable (as a substring) it is omitted. +# +proc define-append {name args} { + if {[get-define $name ""] ne ""} { + # Make a token attempt to avoid duplicates + foreach arg $args { + if {[string first $arg $::define($name)] == -1} { + append ::define($name) " " $arg + } + } + } else { + set ::define($name) [join $args] + } + #dputs "$name += [join $args] => $::define($name)" +} + +# @get-define name ?default=0? +# +# Returns the current value of the 'defined' variable, or $default +# if not set. +# +proc get-define {name {default 0}} { + if {[info exists ::define($name)]} { + #dputs "$name => $::define($name)" + return $::define($name) + } + #dputs "$name => $default" + return $default +} + +# @is-defined name +# +# Returns 1 if the given variable is defined. +# +proc is-defined {name} { + info exists ::define($name) +} + +# @all-defines +# +# Returns a dictionary (name value list) of all defined variables. +# +# This is suitable for use with 'dict', 'array set' or 'foreach' +# and allows for arbitrary processing of the defined variables. +# +proc all-defines {} { + array get ::define +} + + +# @get-env name default +# +# If $name was specified on the command line, return it. +# If $name was set in the environment, return it. +# Otherwise return $default. +# +proc get-env {name default} { + if {[dict exists $::autosetup(cmdline) $name]} { + return [dict get $::autosetup(cmdline) $name] + } + getenv $name $default +} + +# @env-is-set name +# +# Returns 1 if the $name was specified on the command line or in the environment. +# Note that an empty environment variable is not considered to be set. +# +proc env-is-set {name} { + if {[dict exists $::autosetup(cmdline) $name]} { + return 1 + } + if {[getenv $name ""] ne ""} { + return 1 + } + return 0 +} + +# @readfile filename ?default=""? +# +# Return the contents of the file, without the trailing newline. +# If the doesn't exist or can't be read, returns $default. +# +proc readfile {filename {default_value ""}} { + set result $default_value + catch { + set f [open $filename] + set result [read -nonewline $f] + close $f + } + return $result +} + +# @writefile filename value +# +# Creates the given file containing $value. +# Does not add an extra newline. +# +proc writefile {filename value} { + set f [open $filename w] + puts -nonewline $f $value + close $f +} + +proc quote-if-needed {str} { + if {[string match {*[\" ]*} $str]} { + return \"[string map [list \" \\" \\ \\\\] $str]\" + } + return $str +} + +proc quote-argv {argv} { + set args {} + foreach arg $argv { + lappend args [quote-if-needed $arg] + } + join $args +} + +# @suffix suf list +# +# Takes a list and returns a new list with $suf appended +# to each element +# +## suffix .c {a b c} => {a.c b.c c.c} +# +proc suffix {suf list} { + set result {} + foreach p $list { + lappend result $p$suf + } + return $result +} + +# @prefix pre list +# +# Takes a list and returns a new list with $pre prepended +# to each element +# +## prefix jim- {a.c b.c} => {jim-a.c jim-b.c} +# +proc prefix {pre list} { + set result {} + foreach p $list { + lappend result $pre$p + } + return $result +} + +# @find-executable name +# +# Searches the path for an executable with the given name. +# Note that the name may include some parameters, e.g. "cc -mbig-endian", +# in which case the parameters are ignored. +# Returns 1 if found, or 0 if not. +# +proc find-executable {name} { + # Ignore any parameters + set name [lindex $name 0] + if {$name eq ""} { + # The empty string is never a valid executable + return 0 + } + foreach p [split-path] { + dputs "Looking for $name in $p" + set exec [file join $p $name] + if {[file-isexec $exec]} { + dputs "Found $name -> $exec" + return 1 + } + } + return 0 +} + +# @find-an-executable ?-required? name ... +# +# Given a list of possible executable names, +# searches for one of these on the path. +# +# Returns the name found, or "" if none found. +# If the first parameter is '-required', an error is generated +# if no executable is found. +# +proc find-an-executable {args} { + set required 0 + if {[lindex $args 0] eq "-required"} { + set args [lrange $args 1 end] + incr required + } + foreach name $args { + if {[find-executable $name]} { + return $name + } + } + if {$required} { + if {[llength $args] == 1} { + user-error "failed to find: [join $args]" + } else { + user-error "failed to find one of: [join $args]" + } + } + return "" +} + +# @configlog msg +# +# Writes the given message to the configuration log, config.log +# +proc configlog {msg} { + if {![info exists ::autosetup(logfh)]} { + set ::autosetup(logfh) [open config.log w] + } + puts $::autosetup(logfh) $msg +} + +# @msg-checking msg +# +# Writes the message with no newline to stdout. +# +proc msg-checking {msg} { + if {$::autosetup(msg-quiet) == 0} { + maybe-show-timestamp + puts -nonewline $msg + set ::autosetup(msg-checking) 1 + } +} + +# @msg-result msg +# +# Writes the message to stdout. +# +proc msg-result {msg} { + if {$::autosetup(msg-quiet) == 0} { + maybe-show-timestamp + puts $msg + set ::autosetup(msg-checking) 0 + show-notices + } +} + +# @msg-quiet command ... +# +# msg-quiet evaluates it's arguments as a command with output +# from msg-checking and msg-result suppressed. +# +# This is useful if a check needs to run a subcheck which isn't +# of interest to the user. +proc msg-quiet {args} { + incr ::autosetup(msg-quiet) + set rc [uplevel 1 $args] + incr ::autosetup(msg-quiet) -1 + return $rc +} + +# Will be overridden by 'use misc' +proc error-stacktrace {msg} { + return $msg +} + +proc error-location {msg} { + return $msg +} + +################################################################## +# +# Debugging output +# +proc dputs {msg} { + if {$::autosetup(debug)} { + puts $msg + } +} + +################################################################## +# +# User and system warnings and errors +# +# Usage errors such as wrong command line options + +# @user-error msg +# +# Indicate incorrect usage to the user, including if required components +# or features are not found. +# autosetup exits with a non-zero return code. +# +proc user-error {msg} { + show-notices + puts stderr "Error: $msg" + puts stderr "Try: '[file tail $::autosetup(exe)] --help' for options" + exit 1 +} + +# @user-notice msg +# +# Output the given message to stderr. +# +proc user-notice {msg} { + lappend ::autosetup(notices) $msg +} + +# Incorrect usage in the auto.def file. Identify the location. +proc autosetup-error {msg} { + show-notices + puts stderr [error-location $msg] + exit 1 +} + +proc show-notices {} { + if {$::autosetup(msg-checking)} { + puts "" + set ::autosetup(msg-checking) 0 + } + flush stdout + if {[info exists ::autosetup(notices)]} { + puts stderr [join $::autosetup(notices) \n] + unset ::autosetup(notices) + } +} + +proc maybe-show-timestamp {} { + if {$::autosetup(msg-timing) && $::autosetup(msg-checking) == 0} { + puts -nonewline [format {[%6.2f] } [expr {([clock millis] - $::autosetup(start)) % 10000 / 1000.0}]] + } +} + +proc autosetup_version {} { + return "autosetup v$::autosetup(version)" +} + +################################################################## +# +# Directory/path handling +# + +proc realdir {dir} { + set oldpwd [pwd] + cd $dir + set pwd [pwd] + cd $oldpwd + return $pwd +} + +# Follow symlinks until we get to something which is not a symlink +proc realpath {path} { + while {1} { + if {[catch { + set path [file link $path] + }]} { + # Not a link + break + } + } + return $path +} + +# Convert absolute path, $path into a path relative +# to the given directory (or the current dir, if not given). +# +proc relative-path {path {pwd {}}} { + set diff 0 + set same 0 + set newf {} + set prefix {} + set path [file-normalize $path] + if {$pwd eq ""} { + set pwd [pwd] + } else { + set pwd [file-normalize $pwd] + } + + if {$path eq $pwd} { + return . + } + + # Try to make the filename relative to the current dir + foreach p [split $pwd /] f [split $path /] { + if {$p ne $f} { + incr diff + } elseif {!$diff} { + incr same + } + if {$diff} { + if {$p ne ""} { + # Add .. for sibling or parent dir + lappend prefix .. + } + if {$f ne ""} { + lappend newf $f + } + } + } + if {$same == 1 || [llength $prefix] > 3} { + return $path + } + + file join [join $prefix /] [join $newf /] +} + +# Add filename as a dependency to rerun autosetup +# The name will be normalised (converted to a full path) +# +proc autosetup_add_dep {filename} { + lappend ::autosetup(deps) [file-normalize $filename] +} + +################################################################## +# +# Library module support +# + +# @use module ... +# +# Load the given library modules. +# e.g. use cc cc-shared +# +proc use {args} { + foreach m $args { + if {[info exists ::libmodule($m)]} { + continue + } + set ::libmodule($m) 1 + if {[info exists ::modsource($m)]} { + uplevel #0 eval $::modsource($m) + } else { + set source $::autosetup(libdir)/${m}.tcl + if {[file exists $source]} { + uplevel #0 [list source $source] + autosetup_add_dep $source + } else { + puts "Looking for $source" + autosetup-error "use: No such module: $m" + } + } + } +} + +# Initial settings +set autosetup(exe) $::argv0 +set autosetup(istcl) 1 +set autosetup(start) [clock millis] +set autosetup(installed) 0 +set autosetup(msg-checking) 0 +set autosetup(msg-quiet) 0 + +# Embedded modules are inserted below here +set autosetup(installed) 1 +# ----- module asciidoc-formatting ----- + +set modsource(asciidoc-formatting) { +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ +# All rights reserved + +# Module which provides text formatting +# asciidoc format + +use formatting + +proc para {text} { + regsub -all "\[ \t\n\]+" [string trim $text] " " +} +proc title {text} { + underline [para $text] = + nl +} +proc p {text} { + puts [para $text] + nl +} +proc code {text} { + foreach line [parse_code_block $text] { + puts " $line" + } + nl +} +proc codelines {lines} { + foreach line $lines { + puts " $line" + } + nl +} +proc nl {} { + puts "" +} +proc underline {text char} { + regexp "^(\[ \t\]*)(.*)" $text -> indent words + puts $text + puts $indent[string repeat $char [string length $words]] +} +proc section {text} { + underline "[para $text]" - + nl +} +proc subsection {text} { + underline "$text" ~ + nl +} +proc bullet {text} { + puts "* [para $text]" +} +proc indent {text} { + puts " :: " + puts [para $text] +} +proc defn {first args} { + set sep "" + if {$first ne ""} { + puts "${first}::" + } else { + puts " :: " + } + set defn [string trim [join $args \n]] + regsub -all "\n\n" $defn "\n ::\n" defn + puts $defn +} +} + +# ----- module formatting ----- + +set modsource(formatting) { +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ +# All rights reserved + +# Module which provides common text formatting + +# This is designed for documenation which looks like: +# code {...} +# or +# code { +# ... +# ... +# } +# In the second case, we need to work out the indenting +# and strip it from all lines but preserve the remaining indenting. +# Note that all lines need to be indented with the same initial +# spaces/tabs. +# +# Returns a list of lines with the indenting removed. +# +proc parse_code_block {text} { + # If the text begins with newline, take the following text, + # otherwise just return the original + if {![regexp "^\n(.*)" $text -> text]} { + return [list [string trim $text]] + } + + # And trip spaces off the end + set text [string trimright $text] + + set min 100 + # Examine each line to determine the minimum indent + foreach line [split $text \n] { + if {$line eq ""} { + # Ignore empty lines for the indent calculation + continue + } + regexp "^(\[ \t\]*)" $line -> indent + set len [string length $indent] + if {$len < $min} { + set min $len + } + } + + # Now make a list of lines with this indent removed + set lines {} + foreach line [split $text \n] { + lappend lines [string range $line $min end] + } + + # Return the result + return $lines +} +} + +# ----- module getopt ----- + +set modsource(getopt) { +# Copyright (c) 2006 WorkWare Systems http://www.workware.net.au/ +# All rights reserved + +# Simple getopt module + +# Parse everything out of the argv list which looks like an option +# Knows about --enable-thing and --disable-thing as alternatives for --thing=0 or --thing=1 +# Everything which doesn't look like an option, or is after --, is left unchanged +proc getopt {argvname} { + upvar $argvname argv + set nargv {} + + for {set i 0} {$i < [llength $argv]} {incr i} { + set arg [lindex $argv $i] + + #dputs arg=$arg + + if {$arg eq "--"} { + # End of options + incr i + lappend nargv {*}[lrange $argv $i end] + break + } + + if {[regexp {^--([^=][^=]+)=(.*)$} $arg -> name value]} { + lappend opts($name) $value + } elseif {[regexp {^--(enable-|disable-)?([^=]*)$} $arg -> prefix name]} { + if {$prefix eq "disable-"} { + set value 0 + } else { + set value 1 + } + lappend opts($name) $value + } else { + lappend nargv $arg + } + } + + #puts "getopt: argv=[join $argv] => [join $nargv]" + #parray opts + + set argv $nargv + + return [array get opts] +} + +proc opt_val {optarrayname options {default {}}} { + upvar $optarrayname opts + + set result {} + + foreach o $options { + if {[info exists opts($o)]} { + lappend result {*}$opts($o) + } + } + if {[llength $result] == 0} { + return $default + } + return $result +} + +proc opt_bool {optarrayname args} { + upvar $optarrayname opts + + # Support the args being passed as a list + if {[llength $args] == 1} { + set args [lindex $args 0] + } + + foreach o $args { + if {[info exists opts($o)]} { + if {"1" in $opts($o) || "yes" in $opts($o)} { + return 1 + } + } + } + return 0 +} +} + +# ----- module help ----- + +set modsource(help) { +# Copyright (c) 2010 WorkWare Systems http://workware.net.au/ +# All rights reserved + +# Module which provides usage, help and the command reference + +proc autosetup_help {what} { + use_pager + + puts "Usage: [file tail $::autosetup(exe)] \[options\] \[settings\]\n" + puts "This is [autosetup_version], a build environment \"autoconfigurator\"" + puts "See the documentation online at http://msteveb.github.com/autosetup/\n" + + if {$what eq "local"} { + if {[file exists $::autosetup(autodef)]} { + # This relies on auto.def having a call to 'options' + # which will display options and quit + source $::autosetup(autodef) + } else { + options-show + } + } else { + incr ::autosetup(showhelp) + if {[catch {use $what}]} { + user-error "Unknown module: $what" + } else { + options-show + } + } + exit 0 +} + +# If not already paged and stdout is a tty, pipe the output through the pager +# This is done by reinvoking autosetup with --nopager added +proc use_pager {} { + if {![opt-bool nopager] && [getenv PAGER ""] ne "" && ![string match "not a tty" [exec tty]]} { + catch { + exec [info nameofexecutable] $::argv0 --nopager {*}$::argv | [getenv PAGER] >@stdout <@stdin 2>/dev/null + } + exit 0 + } +} + +# Outputs the autosetup references in one of several formats +proc autosetup_reference {{type text}} { + + use_pager + + switch -glob -- $type { + wiki {use wiki-formatting} + ascii* {use asciidoc-formatting} + md - markdown {use markdown-formatting} + default {use text-formatting} + } + + title "[autosetup_version] -- Command Reference" + + section {Introduction} + + p { + See http://msteveb.github.com/autosetup/ for the online documentation for 'autosetup' + } + + p { + 'autosetup' provides a number of built-in commands which + are documented below. These may be used from 'auto.def' to test + for features, define variables, create files from templates and + other similar actions. + } + + automf_command_reference + + exit 0 +} + +proc autosetup_output_block {type lines} { + if {[llength $lines]} { + switch $type { + code { + codelines $lines + } + p { + p [join $lines] + } + list { + foreach line $lines { + bullet $line + } + nl + } + } + } +} + +# Generate a command reference from inline documentation +proc automf_command_reference {} { + lappend files $::autosetup(prog) + lappend files {*}[lsort [glob -nocomplain $::autosetup(libdir)/*.tcl]] + + section "Core Commands" + set type p + set lines {} + set cmd {} + + foreach file $files { + set f [open $file] + while {![eof $f]} { + set line [gets $f] + + # Find lines starting with "# @*" and continuing through the remaining comment lines + if {![regexp {^# @(.*)} $line -> cmd]} { + continue + } + + # Synopsis or command? + if {$cmd eq "synopsis:"} { + section "Module: [file rootname [file tail $file]]" + } else { + subsection $cmd + } + + set lines {} + set type p + + # Now the description + while {![eof $f]} { + set line [gets $f] + + if {![regexp {^#(#)? ?(.*)} $line -> hash cmd]} { + break + } + if {$hash eq "#"} { + set t code + } elseif {[regexp {^- (.*)} $cmd -> cmd]} { + set t list + } else { + set t p + } + + #puts "hash=$hash, oldhash=$oldhash, lines=[llength $lines], cmd=$cmd" + + if {$t ne $type || $cmd eq ""} { + # Finish the current block + autosetup_output_block $type $lines + set lines {} + set type $t + } + if {$cmd ne ""} { + lappend lines $cmd + } + } + + autosetup_output_block $type $lines + } + close $f + } +} +} + +# ----- module init ----- + +set modsource(init) { +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ +# All rights reserved + +# Module to help create auto.def and configure + +proc autosetup_init {} { + set create_configure 1 + if {[file exists configure]} { + if {!$::autosetup(force)} { + # Could this be an autosetup configure? + if {![string match "*\nWRAPPER=*" [readfile configure]]} { + puts "I see configure, but not created by autosetup, so I won't overwrite it." + puts "Use autosetup --init --force to overwrite." + set create_configure 0 + } + } else { + puts "I will overwrite the existing configure because you used --force." + } + } else { + puts "I don't see configure, so I will create it." + } + if {$create_configure} { + if {!$::autosetup(installed)} { + user-notice "Warning: Initialising from the development version of autosetup" + + writefile configure "#!/bin/sh\nWRAPPER=\"\$0\" exec $::autosetup(dir)/autosetup \"\$@\"\n" + } else { + writefile configure \ +{#!/bin/sh +dir="`dirname "$0"`/autosetup" +WRAPPER="$0" exec "`$dir/find-tclsh`" "$dir/autosetup" "$@" +} + } + catch {exec chmod 755 configure} + } + if {![file exists auto.def]} { + puts "I don't see auto.def, so I will create a default one." + writefile auto.def {# Initial auto.def created by 'autosetup --init' + +use cc + +# Add any user options here +options { +} + +make-config-header config.h +make-template Makefile.in +} + } + if {![file exists Makefile.in]} { + puts "Note: I don't see Makefile.in. You will probably need to create one." + } + + exit 0 +} +} + +# ----- module install ----- + +set modsource(install) { +# Copyright (c) 2006-2010 WorkWare Systems http://www.workware.net.au/ +# All rights reserved + +# Module which can install autosetup + +proc autosetup_install {dir} { + if {[catch { + cd $dir + file mkdir autosetup + + set f [open autosetup/autosetup w] + + set publicmodules {} + + # First the main script, but only up until "CUT HERE" + set in [open $::autosetup(dir)/autosetup] + while {[gets $in buf] >= 0} { + if {$buf ne "##-- CUT HERE --##"} { + puts $f $buf + continue + } + + # Insert the static modules here + # i.e. those which don't contain @synopsis: + puts $f "set autosetup(installed) 1" + foreach file [lsort [glob $::autosetup(libdir)/*.tcl]] { + set buf [readfile $file] + if {[string match "*\n# @synopsis:*" $buf]} { + lappend publicmodules $file + continue + } + set modname [file rootname [file tail $file]] + puts $f "# ----- module $modname -----" + puts $f "\nset modsource($modname) \{" + puts $f $buf + puts $f "\}\n" + } + } + close $in + close $f + exec chmod 755 autosetup/autosetup + + # Install public modules + foreach file $publicmodules { + autosetup_install_file $file autosetup + } + + # Install support files + foreach file {config.guess config.sub jimsh0.c find-tclsh test-tclsh LICENSE} { + autosetup_install_file $::autosetup(dir)/$file autosetup + } + exec chmod 755 autosetup/config.sub autosetup/config.guess autosetup/find-tclsh + + writefile autosetup/README.autosetup \ + "This is [autosetup_version]. See http://msteveb.github.com/autosetup/\n" + + } error]} { + user-error "Failed to install autosetup: $error" + } + puts "Installed [autosetup_version] to autosetup/" + catch {exec [info nameofexecutable] autosetup/autosetup --init >@stdout 2>@stderr} + + exit 0 +} + +# Append the contents of $file to filehandle $f +proc autosetup_install_append {f file} { + set in [open $file] + puts $f [read $in] + close $in +} + +proc autosetup_install_file {file dir} { + if {![file exists $file]} { + error "Missing installation file '$file'" + } + writefile [file join $dir [file tail $file]] [readfile $file]\n +} + +if {$::autosetup(installed)} { + user-error "autosetup can only be installed from development source, not from installed copy" +} +} + +# ----- module markdown-formatting ----- + +set modsource(markdown-formatting) { +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ +# All rights reserved + +# Module which provides text formatting +# markdown format (kramdown syntax) + +use formatting + +proc para {text} { + regsub -all "\[ \t\n\]+" [string trim $text] " " text + regsub -all {([^a-zA-Z])'([^']*)'} $text {\1**`\2`**} text + regsub -all {^'([^']*)'} $text {**`\1`**} text + regsub -all {(http[^ \t\n]*)} $text {[\1](\1)} text + return $text +} +proc title {text} { + underline [para $text] = + nl +} +proc p {text} { + puts [para $text] + nl +} +proc codelines {lines} { + puts "~~~~~~~~~~~~" + foreach line $lines { + puts $line + } + puts "~~~~~~~~~~~~" + nl +} +proc code {text} { + puts "~~~~~~~~~~~~" + foreach line [parse_code_block $text] { + puts $line + } + puts "~~~~~~~~~~~~" + nl +} +proc nl {} { + puts "" +} +proc underline {text char} { + regexp "^(\[ \t\]*)(.*)" $text -> indent words + puts $text + puts $indent[string repeat $char [string length $words]] +} +proc section {text} { + underline "[para $text]" - + nl +} +proc subsection {text} { + puts "### `$text`" + nl +} +proc bullet {text} { + puts "* [para $text]" +} +proc defn {first args} { + puts "^" + set defn [string trim [join $args \n]] + if {$first ne ""} { + puts "**${first}**" + puts -nonewline ": " + regsub -all "\n\n" $defn "\n: " defn + } + puts "$defn" +} +} + +# ----- module misc ----- + +set modsource(misc) { +# Copyright (c) 2007-2010 WorkWare Systems http://www.workware.net.au/ +# All rights reserved + +# Module containing misc procs useful to modules +# Largely for platform compatibility + +set autosetup(istcl) [info exists ::tcl_library] +set autosetup(iswin) [string equal windows $tcl_platform(platform)] + +if {$autosetup(iswin)} { + # mingw/windows separates $PATH with semicolons + # and doesn't have an executable bit + proc split-path {} { + split [getenv PATH .] {;} + } + proc file-isexec {exec} { + # Basic test for windows. We ignore .bat + if {[file isfile $exec] || [file isfile $exec.exe]} { + return 1 + } + return 0 + } +} else { + # unix separates $PATH with colons and has and executable bit + proc split-path {} { + split [getenv PATH .] : + } + proc file-isexec {exec} { + file executable $exec + } +} + +# Assume that exec can return stdout and stderr +proc exec-with-stderr {args} { + exec {*}$args 2>@1 +} + +if {$autosetup(istcl)} { + # Tcl doesn't have the env command + proc getenv {name args} { + if {[info exists ::env($name)]} { + return $::env($name) + } + if {[llength $args]} { + return [lindex $args 0] + } + return -code error "environment variable \"$name\" does not exist" + } +} elseif {$autosetup(iswin)} { + # On Windows, backslash convert all environment variables + # (Assume that Tcl does this for us) + proc getenv {name args} { + string map {\\ /} [env $name {*}$args] + } +} else { + # Jim on unix is simple + alias getenv env +} + +# In case 'file normalize' doesn't exist +# +proc file-normalize {path} { + if {[catch {file normalize $path} result]} { + if {$path eq ""} { + return "" + } + set oldpwd [pwd] + if {[file isdir $path]} { + cd $path + set result [pwd] + } else { + cd [file dirname $path] + set result [file join [pwd] [file tail $path]] + } + cd $oldpwd + } + return $result +} + +# If everything is working properly, the only errors which occur +# should be generated in user code (e.g. auto.def). +# By default, we only want to show the error location in user code. +# We use [info frame] to achieve this, but it works differently on Tcl and Jim. +# +# This is designed to be called for incorrect usage in auto.def, via autosetup-error +# +proc error-location {msg} { + if {$::autosetup(debug)} { + return -code error $msg + } + # Search back through the stack trace for the first error in a .def file + for {set i 1} {$i < [info level]} {incr i} { + if {$::autosetup(istcl)} { + array set info [info frame -$i] + } else { + lassign [info frame -$i] info(caller) info(file) info(line) + } + if {[string match *.def $info(file)]} { + return "[relative-path $info(file)]:$info(line): Error: $msg" + } + #puts "Skipping $info(file):$info(line)" + } + return $msg +} + +# Similar to error-location, but called when user code generates an error +# In this case we want to show the stack trace in user code, but not in autosetup code +# (unless --debug is enabled) +# +proc error-stacktrace {msg} { + if {$::autosetup(istcl)} { + if {[regexp {file "([^ ]*)" line ([0-9]*)} $::errorInfo dummy file line]} { + return "[relative-path $file]:$line $msg\n$::errorInfo" + } + return $::errorInfo + } else { + # Prepend a live stacktrace to the error stacktrace, omitting the current level + set stacktrace [concat [info stacktrace] [lrange [stacktrace] 3 end]] + + if {!$::autosetup(debug)} { + # Omit any levels from autosetup or with no file + set newstacktrace {} + foreach {p f l} $stacktrace { + if {[string match "*autosetup" $f] || $f eq ""} { + #puts "Skipping $p $f:$l" + continue + } + lappend newstacktrace $p $f $l + } + set stacktrace $newstacktrace + } + + # Convert filenames to relative paths + set newstacktrace {} + foreach {p f l} $stacktrace { + lappend newstacktrace $p [relative-path $f] $l + } + lassign $newstacktrace p f l + if {$f ne ""} { + set prefix "$f:$l: " + } else { + set prefix "" + } + + return "${prefix}Error: $msg\n[stackdump $newstacktrace]" + } +} +} + +# ----- module text-formatting ----- + +set modsource(text-formatting) { +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ +# All rights reserved + +# Module which provides text formatting + +use formatting + +proc wordwrap {text length {firstprefix ""} {nextprefix ""}} { + set len 0 + set space $firstprefix + foreach word [split $text] { + set word [string trim $word] + if {$word == ""} { + continue + } + if {$len && [string length $space$word] + $len >= $length} { + puts "" + set len 0 + set space $nextprefix + } + incr len [string length $space$word] + + # Use man-page conventions for highlighting 'quoted' and *quoted* + # single words. + # Use x^Hx for *bold* and _^Hx for 'underline'. + # + # less and more will both understand this. + # Pipe through 'col -b' to remove them. + if {[regexp {^'(.*)'([^a-zA-Z0-9_]*)$} $word -> bareword dot]} { + regsub -all . $bareword "_\b&" word + append word $dot + } elseif {[regexp {^[*](.*)[*]([^a-zA-Z0-9_]*)$} $word -> bareword dot]} { + regsub -all . $bareword "&\b&" word + append word $dot + } + puts -nonewline $space$word + set space " " + } + if {$len} { + puts "" + } +} +proc title {text} { + underline [string trim $text] = + nl +} +proc p {text} { + wordwrap $text 80 + nl +} +proc codelines {lines} { + foreach line $lines { + puts " $line" + } + nl +} +proc nl {} { + puts "" +} +proc underline {text char} { + regexp "^(\[ \t\]*)(.*)" $text -> indent words + puts $text + puts $indent[string repeat $char [string length $words]] +} +proc section {text} { + underline "[string trim $text]" - + nl +} +proc subsection {text} { + underline "$text" ~ + nl +} +proc bullet {text} { + wordwrap $text 76 " * " " " +} +proc indent {text} { + wordwrap $text 76 " " " " +} +proc defn {first args} { + if {$first ne ""} { + underline " $first" ~ + } + foreach p $args { + if {$p ne ""} { + indent $p + } + } +} +} + +# ----- module wiki-formatting ----- + +set modsource(wiki-formatting) { +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ +# All rights reserved + +# Module which provides text formatting +# wiki.tcl.tk format output + +use formatting + +proc joinlines {text} { + set lines {} + foreach l [split [string trim $text] \n] { + lappend lines [string trim $l] + } + join $lines +} +proc p {text} { + puts [joinlines $text] + puts "" +} +proc title {text} { + puts "*** [joinlines $text] ***" + puts "" +} +proc codelines {lines} { + puts "======" + foreach line $lines { + puts " $line" + } + puts "======" +} +proc code {text} { + puts "======" + foreach line [parse_code_block $text] { + puts " $line" + } + puts "======" +} +proc nl {} { +} +proc section {text} { + puts "'''$text'''" + puts "" +} +proc subsection {text} { + puts "''$text''" + puts "" +} +proc bullet {text} { + puts " * [joinlines $text]" +} +proc indent {text} { + puts " : [joinlines $text]" +} +proc defn {first args} { + if {$first ne ""} { + indent '''$first''' + } + + foreach p $args { + p $p + } +} +} + + +################################################################## +# +# Entry/Exit +# +if {$autosetup(debug)} { + main $argv +} +if {[catch {main $argv} msg] == 1} { + show-notices + puts stderr [error-stacktrace $msg] + if {!$autosetup(debug) && !$autosetup(istcl)} { + puts stderr "Try: '[file tail $autosetup(exe)] --debug' for a full stack trace" + } + exit 1 +} diff --git a/release/src/router/usbmodeswitch/jim/autosetup/cc-lib.tcl b/release/src/router/usbmodeswitch/jim/autosetup/cc-lib.tcl new file mode 100644 index 0000000000..e8e5e8608f --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/autosetup/cc-lib.tcl @@ -0,0 +1,77 @@ +# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ +# All rights reserved + +# @synopsis: +# +# Provides a library of common tests on top of the 'cc' module. + +use cc + +module-options {} + +# @cc-check-lfs +# +# The equivalent of the AC_SYS_LARGEFILE macro +# +# defines 'HAVE_LFS' if LFS is available, +# and defines '_FILE_OFFSET_BITS=64' if necessary +# +# Returns 1 if 'LFS' is available or 0 otherwise +# +proc cc-check-lfs {} { + cc-check-includes sys/types.h + msg-checking "Checking if -D_FILE_OFFSET_BITS=64 is needed..." + set lfs 1 + if {[msg-quiet cc-with {-includes sys/types.h} {cc-check-sizeof off_t}] == 8} { + msg-result no + } elseif {[msg-quiet cc-with {-includes sys/types.h -cflags -D_FILE_OFFSET_BITS=64} {cc-check-sizeof off_t}] == 8} { + define _FILE_OFFSET_BITS 64 + msg-result yes + } else { + set lfs 0 + msg-result none + } + define-feature lfs $lfs + return $lfs +} + +# @cc-check-endian +# +# The equivalent of the AC_C_BIGENDIAN macro +# +# defines 'HAVE_BIG_ENDIAN' if endian is known to be big, +# or 'HAVE_LITTLE_ENDIAN' if endian is known to be little. +# +# Returns 1 if determined, or 0 if not. +# +proc cc-check-endian {} { + cc-check-includes sys/types.h sys/param.h + set rc 0 + msg-checking "Checking endian..." + cc-with {-includes {sys/types.h sys/param.h}} { + if {[cctest -code { + #if !defined(BIG_ENDIAN) || !defined(BYTE_ORDER) + #error unknown + #elif BYTE_ORDER != BIG_ENDIAN + #error little + #endif + }]} { + define-feature big-endian + msg-result "big" + set rc 1 + } elseif {[cctest -code { + #if !defined(LITTLE_ENDIAN) || !defined(BYTE_ORDER) + #error unknown + #elif BYTE_ORDER != LITTLE_ENDIAN + #error big + #endif + }]} { + define-feature little-endian + msg-result "little" + set rc 1 + } else { + msg-result "unknown" + } + } + return $rc +} diff --git a/release/src/router/usbmodeswitch/jim/autosetup/cc-shared.tcl b/release/src/router/usbmodeswitch/jim/autosetup/cc-shared.tcl new file mode 100644 index 0000000000..1e7744099c --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/autosetup/cc-shared.tcl @@ -0,0 +1,63 @@ +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ +# All rights reserved + +# @synopsis: +# +# The 'cc-shared' module provides support for shared libraries and shared objects. +# It defines the following variables: +# +## SH_CFLAGS Flags to use compiling sources destined for a shared library +## SH_LDFLAGS Flags to use linking a shared library +## SHOBJ_CFLAGS Flags to use compiling sources destined for a shared object +## SHOBJ_LDFLAGS Flags to use linking a shared object +## SH_LINKFLAGS Flags to use linking an executable which will load shared objects +## LD_LIBRARY_PATH Environment variable which specifies path to shared libraries + +module-options {} + +foreach i {SH_LINKFLAGS SH_CFLAGS SH_LDFLAGS SHOBJ_CFLAGS SHOBJ_LDFLAGS} { + define $i "" +} + +define LD_LIBRARY_PATH LD_LIBRARY_PATH + +switch -glob -- [get-define host] { + *-*-darwin* { + define SH_CFLAGS -dynamic + define SH_LDFLAGS "-dynamiclib" + define SHOBJ_CFLAGS "-dynamic -fno-common" + define SHOBJ_LDFLAGS "-bundle -undefined dynamic_lookup" + define LD_LIBRARY_PATH DYLD_LIBRARY_PATH + } + *-*-ming* { + define SH_LDFLAGS -shared + define SHOBJ_LDFLAGS -shared + } + *-*-cygwin { + define SH_LDFLAGS -shared + define SHOBJ_LDFLAGS -shared + } + *-*-solaris* { + # XXX: These haven't been fully tested. + #define SH_LINKFLAGS -Wl,-export-dynamic + define SH_CFLAGS -Kpic + define SHOBJ_CFLAGS -Kpic + define SHOBJ_LDFLAGS "-G" + } + *-*-hpux { + # XXX: These haven't been tested + define SH_LINKFLAGS -Wl,+s + define SH_CFLAGS +z + define SHOBJ_CFLAGS "+O3 +z" + define SHOBJ_LDFLAGS -b + define LD_LIBRARY_PATH SHLIB_PATH + } + * { + # Generic Unix settings + define SH_LINKFLAGS -rdynamic + define SH_CFLAGS -fpic + define SH_LDFLAGS -shared + define SHOBJ_CFLAGS -fpic + define SHOBJ_LDFLAGS "-shared" + } +} diff --git a/release/src/router/usbmodeswitch/jim/autosetup/cc.tcl b/release/src/router/usbmodeswitch/jim/autosetup/cc.tcl new file mode 100644 index 0000000000..707e69ce72 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/autosetup/cc.tcl @@ -0,0 +1,660 @@ +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ +# All rights reserved + +# @synopsis: +# +# The 'cc' module supports checking various 'features' of the C or C++ +# compiler/linker environment. Common commands are cc-check-includes, +# cc-check-types, cc-check-functions, cc-with, make-autoconf-h and make-template. +# +# The following environment variables are used if set: +# +## CC - C compiler +## CXX - C++ compiler +## CCACHE - Set to "none" to disable automatic use of ccache +## CFLAGS - Additional C compiler flags +## CXXFLAGS - Additional C++ compiler flags +## LDFLAGS - Additional compiler flags during linking +## LIBS - Additional libraries to use (for all tests) +## CROSS - Tool prefix for cross compilation +# +# The following variables are defined from the corresponding +# environment variables if set. +# +## CPPFLAGS +## LINKFLAGS +## CC_FOR_BUILD +## LD + +use system + +module-options {} + +# Note that the return code is not meaningful +proc cc-check-something {name code} { + uplevel 1 $code +} + +# Checks for the existence of the given function by linking +# +proc cctest_function {function} { + cctest -link 1 -declare "extern void $function\(void);" -code "$function\();" +} + +# Checks for the existence of the given type by compiling +proc cctest_type {type} { + cctest -code "$type _x;" +} + +# Checks for the existence of the given type/structure member. +# e.g. "struct stat.st_mtime" +proc cctest_member {struct_member} { + lassign [split $struct_member .] struct member + cctest -code "static $struct _s; return sizeof(_s.$member);" +} + +# Checks for the existence of the given define by compiling +# +proc cctest_define {name} { + cctest -code "#ifndef $name\n#error not defined\n#endif" +} + +# Checks for the existence of the given name either as +# a macro (#define) or an rvalue (such as an enum) +# +proc cctest_decl {name} { + cctest -code "#ifndef $name\n(void)$name;\n#endif" +} + +# @cc-check-sizeof type ... +# +# Checks the size of the given types (between 1 and 32, inclusive). +# Defines a variable with the size determined, or "unknown" otherwise. +# e.g. for type 'long long', defines SIZEOF_LONG_LONG. +# Returns the size of the last type. +# +proc cc-check-sizeof {args} { + foreach type $args { + msg-checking "Checking for sizeof $type..." + set size unknown + # Try the most common sizes first + foreach i {4 8 1 2 16 32} { + if {[cctest -code "static int _x\[sizeof($type) == $i ? 1 : -1\] = { 1 };"]} { + set size $i + break + } + } + msg-result $size + set define [feature-define-name $type SIZEOF_] + define $define $size + } + # Return the last result + get-define $define +} + +# Checks for each feature in $list by using the given script. +# +# When the script is evaluated, $each is set to the feature +# being checked, and $extra is set to any additional cctest args. +# +# Returns 1 if all features were found, or 0 otherwise. +proc cc-check-some-feature {list script} { + set ret 1 + foreach each $list { + if {![check-feature $each $script]} { + set ret 0 + } + } + return $ret +} + +# @cc-check-includes includes ... +# +# Checks that the given include files can be used +proc cc-check-includes {args} { + cc-check-some-feature $args { + cctest -includes $each + } +} + +# @cc-check-types type ... +# +# Checks that the types exist. +proc cc-check-types {args} { + cc-check-some-feature $args { + cctest_type $each + } +} + +# @cc-check-defines define ... +# +# Checks that the given preprocessor symbol is defined +proc cc-check-defines {args} { + cc-check-some-feature $args { + cctest_define $each + } +} + +# @cc-check-decls name ... +# +# Checks that each given name is either a preprocessor symbol or rvalue +# such as an enum. Note that the define used for a decl is HAVE_DECL_xxx +# rather than HAVE_xxx +proc cc-check-decls {args} { + set ret 1 + foreach name $args { + msg-checking "Checking for $name..." + set r [cctest_decl $name] + define-feature "decl $name" $r + if {$r} { + msg-result "ok" + } else { + msg-result "not found" + set ret 0 + } + } + return $ret +} + +# @cc-check-functions function ... +# +# Checks that the given functions exist (can be linked) +proc cc-check-functions {args} { + cc-check-some-feature $args { + cctest_function $each + } +} + +# @cc-check-members type.member ... +# +# Checks that the given type/structure members exist. +# A structure member is of the form "struct stat.st_mtime" +proc cc-check-members {args} { + cc-check-some-feature $args { + cctest_member $each + } +} + +# @cc-check-function-in-lib function libs ?otherlibs? +# +# Checks that the given given function can be found in one of the libs. +# +# First checks for no library required, then checks each of the libraries +# in turn. +# +# If the function is found, the feature is defined and lib_$function is defined +# to -l$lib where the function was found, or "" if no library required. +# In addition, -l$lib is added to the LIBS define. +# +# If additional libraries may be needed for linking, they should be specified +# as $extralibs as "-lotherlib1 -lotherlib2". +# These libraries are not automatically added to LIBS. +# +# Returns 1 if found or 0 if not. +# +proc cc-check-function-in-lib {function libs {otherlibs {}}} { + msg-checking "Checking libs for $function..." + set found 0 + cc-with [list -libs $otherlibs] { + if {[cctest_function $function]} { + msg-result "none needed" + define lib_$function "" + incr found + } else { + foreach lib $libs { + cc-with [list -libs -l$lib] { + if {[cctest_function $function]} { + msg-result -l$lib + define lib_$function -l$lib + define-append LIBS -l$lib + incr found + break + } + } + } + } + } + if {$found} { + define [feature-define-name $function] + } else { + msg-result "no" + } + return $found +} + +# @cc-check-tools tool ... +# +# Checks for existence of the given compiler tools, taking +# into account any cross compilation prefix. +# +# For example, when checking for "ar", first AR is checked on the command +# line and then in the environment. If not found, "${host}-ar" or +# simply "ar" is assumed depending upon whether cross compiling. +# The path is searched for this executable, and if found AR is defined +# to the executable name. +# +# It is an error if the executable is not found. +# +proc cc-check-tools {args} { + foreach tool $args { + set TOOL [string toupper $tool] + set exe [get-env $TOOL [get-define cross]$tool] + if {![find-executable $exe]} { + user-error "Failed to find $exe" + } + define $TOOL $exe + } +} + +# @cc-check-progs prog ... +# +# Checks for existence of the given executables on the path. +# +# For example, when checking for "grep", the path is searched for +# the executable, 'grep', and if found GREP is defined as "grep". +# +# It the executable is not found, the variable is defined as false. +# Returns 1 if all programs were found, or 0 otherwise. +# +proc cc-check-progs {args} { + set failed 0 + foreach prog $args { + set PROG [string toupper $prog] + msg-checking "Checking for $prog..." + if {![find-executable $prog]} { + msg-result no + define $PROG false + incr failed + } else { + msg-result ok + define $PROG $prog + } + } + expr {!$failed} +} + +# Adds the given settings to $::autosetup(ccsettings) and +# returns the old settings. +# +proc cc-add-settings {settings} { + if {[llength $settings] % 2} { + autosetup-error "settings list is missing a value: $settings" + } + + set prev [cc-get-settings] + # workaround a bug in some versions of jimsh by forcing + # conversion of $prev to a list + llength $prev + + array set new $prev + + foreach {name value} $settings { + switch -exact -- $name { + -cflags - -includes { + # These are given as lists + lappend new($name) {*}$value + } + -declare { + lappend new($name) $value + } + -libs { + # Note that new libraries are added before previous libraries + set new($name) [list {*}$value {*}$new($name)] + } + -link - -lang { + set new($name) $value + } + -source - -sourcefile - -code { + # XXX: These probably are only valid directly from cctest + set new($name) $value + } + default { + autosetup-error "unknown cctest setting: $name" + } + } + } + + cc-store-settings [array get new] + + return $prev +} + +proc cc-store-settings {new} { + set ::autosetup(ccsettings) $new +} + +proc cc-get-settings {} { + return $::autosetup(ccsettings) +} + +# Similar to cc-add-settings, but each given setting +# simply replaces the existing value. +# +# Returns the previous settings +proc cc-update-settings {args} { + set prev [cc-get-settings] + cc-store-settings [dict merge $prev $args] + return $prev +} + +# @cc-with settings ?{ script }? +# +# Sets the given 'cctest' settings and then runs the tests in 'script'. +# Note that settings such as -lang replace the current setting, while +# those such as -includes are appended to the existing setting. +# +# If no script is given, the settings become the default for the remainder +# of the auto.def file. +# +## cc-with {-lang c++} { +## # This will check with the C++ compiler +## cc-check-types bool +## cc-with {-includes signal.h} { +## # This will check with the C++ compiler, signal.h and any existing includes. +## ... +## } +## # back to just the C++ compiler +## } +# +# The -libs setting is special in that newer values are added *before* earlier ones. +# +## cc-with {-libs {-lc -lm}} { +## cc-with {-libs -ldl} { +## cctest -libs -lsocket ... +## # libs will be in this order: -lsocket -ldl -lc -lm +## } +## } +proc cc-with {settings args} { + if {[llength $args] == 0} { + cc-add-settings $settings + } elseif {[llength $args] > 1} { + autosetup-error "usage: cc-with settings ?script?" + } else { + set save [cc-add-settings $settings] + set rc [catch {uplevel 1 [lindex $args 0]} result info] + cc-store-settings $save + if {$rc != 0} { + return $result -code [dict get $info -code] + } + return $result + } +} + +# @cctest ?settings? +# +# Low level C compiler checker. Compiles and or links a small C program +# according to the arguments and returns 1 if OK, or 0 if not. +# +# Supported settings are: +# +## -cflags cflags A list of flags to pass to the compiler +## -includes list A list of includes, e.g. {stdlib.h stdio.h} +## -declare code Code to declare before main() +## -link 1 Don't just compile, link too +## -lang c|c++ Use the C (default) or C++ compiler +## -libs liblist List of libraries to link, e.g. {-ldl -lm} +## -code code Code to compile in the body of main() +## -source code Compile a complete program. Ignore -includes, -declare and -code +## -sourcefile file Shorthand for -source [readfile [get-define srcdir]/$file] +# +# Unless -source or -sourcefile is specified, the C program looks like: +# +## #include /* same for remaining includes in the list */ +## +## declare-code /* any code in -declare, verbatim */ +## +## int main(void) { +## code /* any code in -code, verbatim */ +## return 0; +## } +# +# Any failures are recorded in 'config.log' +# +proc cctest {args} { + set src conftest__.c + set tmp conftest__ + + # Easiest way to merge in the settings + cc-with $args { + array set opts [cc-get-settings] + } + + if {[info exists opts(-sourcefile)]} { + set opts(-source) [readfile [get-define srcdir]/$opts(-sourcefile) "#error can't find $opts(-sourcefile)"] + } + if {[info exists opts(-source)]} { + set lines $opts(-source) + } else { + foreach i $opts(-includes) { + if {$opts(-code) ne "" && ![feature-checked $i]} { + # Compiling real code with an unchecked header file + # Quickly (and silently) check for it now + + # Remove all -includes from settings before checking + set saveopts [cc-update-settings -includes {}] + msg-quiet cc-check-includes $i + cc-store-settings $saveopts + } + if {$opts(-code) eq "" || [have-feature $i]} { + lappend source "#include <$i>" + } + } + lappend source {*}$opts(-declare) + lappend source "int main(void) {" + lappend source $opts(-code) + lappend source "return 0;" + lappend source "}" + + set lines [join $source \n] + } + + # Build the command line + set cmdline {} + lappend cmdline {*}[get-define CCACHE] + switch -exact -- $opts(-lang) { + c++ { + lappend cmdline {*}[get-define CXX] {*}[get-define CXXFLAGS] + } + c { + lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS] + } + default { + autosetup-error "cctest called with unknown language: $opts(-lang)" + } + } + + if {!$opts(-link)} { + set tmp conftest__.o + lappend cmdline -c + } + lappend cmdline {*}$opts(-cflags) + + switch -glob -- [get-define host] { + *-*-darwin* { + # Don't generate .dSYM directories + lappend cmdline -gstabs + } + } + lappend cmdline $src -o $tmp {*}$opts(-libs) + + # At this point we have the complete command line and the + # complete source to be compiled. Get the result from cache if + # we can + if {[info exists ::cc_cache($cmdline,$lines)]} { + msg-checking "(cached) " + set ok $::cc_cache($cmdline,$lines) + if {$::autosetup(debug)} { + configlog "From cache (ok=$ok): [join $cmdline]" + configlog "============" + configlog $lines + configlog "============" + } + return $ok + } + + writefile $src $lines\n + + set ok 1 + if {[catch {exec-with-stderr {*}$cmdline} result errinfo]} { + configlog "Failed: [join $cmdline]" + configlog $result + configlog "============" + configlog "The failed code was:" + configlog $lines + configlog "============" + set ok 0 + } elseif {$::autosetup(debug)} { + configlog "Compiled OK: [join $cmdline]" + configlog "============" + configlog $lines + configlog "============" + } + file delete $src + file delete $tmp + + # cache it + set ::cc_cache($cmdline,$lines) $ok + + return $ok +} + +# @make-autoconf-h outfile ?auto-patterns=HAVE_*? ?bare-patterns=SIZEOF_*? +# +# Deprecated - see make-config-header +proc make-autoconf-h {file {autopatterns {HAVE_*}} {barepatterns {SIZEOF_* HAVE_DECL_*}}} { + user-notice "*** make-autoconf-h is deprecated -- use make-config-header instead" + make-config-header $file -auto $autopatterns -bare $barepatterns +} + +# @make-config-header outfile ?-auto patternlist? ?-bare patternlist? ?-none patternlist? ?-str patternlist? ... +# +# Examines all defined variables which match the given patterns +# and writes an include file, $file, which defines each of these. +# Variables which match '-auto' are output as follows: +# - defines which have the value "0" are ignored. +# - defines which have integer values are defined as the integer value. +# - any other value is defined as a string, e.g. "value" +# Variables which match '-bare' are defined as-is. +# Variables which match '-str' are defined as a string, e.g. "value" +# Variables which match '-none' are omitted. +# +# Note that order is important. The first pattern which matches is selected +# Default behaviour is: +# +# -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* -none * +# +# If the file would be unchanged, it is not written. +proc make-config-header {file args} { + set guard _[string toupper [regsub -all {[^a-zA-Z0-9]} [file tail $file] _]] + file mkdir [file dirname $file] + set lines {} + lappend lines "#ifndef $guard" + lappend lines "#define $guard" + + # Add some defaults + lappend args -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* + + foreach n [lsort [dict keys [all-defines]]] { + set value [get-define $n] + set type [calc-define-output-type $n $args] + switch -exact -- $type { + -bare { + # Just output the value unchanged + } + -none { + continue + } + -str { + set value \"$value\" + } + -auto { + # Automatically determine the type + if {$value eq "0"} { + lappend lines "/* #undef $n */" + continue + } + if {![string is integer -strict $value]} { + set value \"$value\" + } + } + "" { + continue + } + default { + autosetup-error "Unknown type in make-config-header: $type" + } + } + lappend lines "#define $n $value" + } + lappend lines "#endif" + set buf [join $lines \n] + write-if-changed $file $buf { + msg-result "Created $file" + } +} + +proc calc-define-output-type {name spec} { + foreach {type patterns} $spec { + foreach pattern $patterns { + if {[string match $pattern $name]} { + return $type + } + } + } + return "" +} + +# Initialise some values from the environment or commandline or default settings +foreach i {LDFLAGS LIBS CPPFLAGS LINKFLAGS {CFLAGS "-g -O2"}} { + lassign $i var default + define $var [get-env $var $default] +} + +if {[env-is-set CC]} { + # Set by the user, so don't try anything else + set try [list [get-env CC ""]] +} else { + # Try some reasonable options + set try [list [get-define cross]cc [get-define cross]gcc] +} +define CC [find-an-executable {*}$try] +if {[get-define CC] eq ""} { + user-error "Could not find a C compiler. Tried: [join $try ", "]" +} + +define CPP [get-env CPP "[get-define CC] -E"] + +# XXX: Could avoid looking for a C++ compiler until requested +# Note that if CXX isn't found, we just set it to "false". It might not be needed. +if {[env-is-set CXX]} { + define CXX [find-an-executable -required [get-env CXX ""]] +} else { + define CXX [find-an-executable [get-define cross]c++ [get-define cross]g++ false] +} + +# CXXFLAGS default to CFLAGS if not specified +define CXXFLAGS [get-env CXXFLAGS [get-define CFLAGS]] + +cc-check-tools ld + +# May need a CC_FOR_BUILD, so look for one +define CC_FOR_BUILD [find-an-executable [get-env CC_FOR_BUILD ""] cc gcc false] + +if {[get-define CC] eq ""} { + user-error "Could not find a C compiler. Tried: [join $try ", "]" +} + +define CCACHE [find-an-executable [get-env CCACHE ccache]] + +# Initial cctest settings +cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {}} + +msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS]" +if {[get-define CXX] ne "false"} { + msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-define CXXFLAGS]" +} +msg-result "Build C compiler...[get-define CC_FOR_BUILD]" + +if {![cc-check-includes stdlib.h]} { + user-error "Compiler does not work. See config.log" +} diff --git a/release/src/router/usbmodeswitch/jim/autosetup/config.guess b/release/src/router/usbmodeswitch/jim/autosetup/config.guess new file mode 100755 index 0000000000..4c8f032e78 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/autosetup/config.guess @@ -0,0 +1,1508 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 +# Free Software Foundation, Inc. + +timestamp='2010-09-24' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Originally written by Per Bothner. Please send patches (context +# diff format) to and include a ChangeLog +# entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free +Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' HUP INT TERM + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" HUP INT PIPE TERM ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm:riscos:*:*|arm:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + case ${UNAME_MACHINE} in + pc98) + echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-gnu + else + echo ${UNAME_MACHINE}-unknown-linux-gnueabi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit ;; + i*86:Linux:*:*) + LIBC=gnu + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + or32:Linux:*:*) + echo or32-unknown-linux-gnu + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-gnu + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-tilera-linux-gnu + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-gnu + exit ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + i386) + eval $set_cc_for_build + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + UNAME_PROCESSOR="x86_64" + fi + fi ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-?:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/release/src/router/usbmodeswitch/jim/autosetup/config.sub b/release/src/router/usbmodeswitch/jim/autosetup/config.sub new file mode 100755 index 0000000000..320e303881 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/autosetup/config.sub @@ -0,0 +1,1739 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 +# Free Software Foundation, Inc. + +timestamp='2010-09-11' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Please send patches to . Submit a context +# diff and a properly formatted GNU ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free +Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 \ + | ns16k | ns32k \ + | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu | strongarm \ + | tahoe | thumb | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e \ + | we32k \ + | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12 | picochip) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile-* | tilegx-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze) + basic_machine=microblaze-xilinx + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + # This must be matched before tile*. + tilegx*) + basic_machine=tilegx-unknown + os=-linux-gnu + ;; + tile*) + basic_machine=tile-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/release/src/router/usbmodeswitch/jim/autosetup/find-tclsh b/release/src/router/usbmodeswitch/jim/autosetup/find-tclsh new file mode 100755 index 0000000000..7ef152a55e --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/autosetup/find-tclsh @@ -0,0 +1,17 @@ +#!/bin/sh +# Looks for a suitable tclsh or jimsh in the PATH +# If not found, builds a bootstrap jimsh from source +d=`dirname "$0"` +{ "$d/jimsh0" "$d/test-tclsh"; } 2>/dev/null && exit 0 +PATH="$PATH:$d" +for tclsh in jimsh tclsh tclsh8.5 tclsh8.6; do + { $tclsh "$d/test-tclsh"; } 2>/dev/null && exit 0 +done +echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0" +./make-bootstrap-jim > $d/jimsh0.c +for cc in ${CC_FOR_BUILD:-cc} gcc; do + { $cc -o "$d/jimsh0" "$d/jimsh0.c"; } 2>/dev/null || continue + "$d/jimsh0" "$d/test-tclsh" && exit 0 +done +echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc." +echo false diff --git a/release/src/router/usbmodeswitch/jim/autosetup/system.tcl b/release/src/router/usbmodeswitch/jim/autosetup/system.tcl new file mode 100644 index 0000000000..f05d05b2cb --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/autosetup/system.tcl @@ -0,0 +1,268 @@ +# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ +# All rights reserved + +# @synopsis: +# +# This module supports common system interrogation and options +# such as --host, --build, --prefix, and setting srcdir, builddir, and EXEXT. +# +# It also support the 'feature' naming convention, where searching +# for a feature such as sys/type.h defines HAVE_SYS_TYPES_H +# +module-options { + host:host-alias => {a complete or partial cpu-vendor-opsys for the system where + the application will run (defaults to the same value as --build)} + build:build-alias => {a complete or partial cpu-vendor-opsys for the system + where the application will be built (defaults to the + result of running config.guess)} + prefix:dir => {the target directory for the build (defaults to /usr/local)} + + # These (hidden) options are supported for autoconf/automake compatibility + exec-prefix: + bindir: + sbindir: + includedir: + mandir: + infodir: + libexecdir: + datadir: + libdir: + sysconfdir: + sharedstatedir: + localstatedir: + maintainer-mode=0 + dependency-tracking=0 +} + +# Returns 1 if exists, or 0 if not +# +proc check-feature {name code} { + msg-checking "Checking for $name..." + set r [uplevel 1 $code] + define-feature $name $r + if {$r} { + msg-result "ok" + } else { + msg-result "not found" + } + return $r +} + +# @have-feature name ?default=0? +# +# Returns the value of the feature if defined, or $default if not. +# See 'feature-define-name' for how the feature name +# is translated into the define name. +# +proc have-feature {name {default 0}} { + get-define [feature-define-name $name] $default +} + +# @define-feature name ?value=1? +# +# Sets the feature 'define' to the given value. +# See 'feature-define-name' for how the feature name +# is translated into the define name. +# +proc define-feature {name {value 1}} { + define [feature-define-name $name] $value +} + +# @feature-checked name +# +# Returns 1 if the feature has been checked, whether true or not +# +proc feature-checked {name} { + is-defined [feature-define-name $name] +} + +# @feature-define-name name ?prefix=HAVE_? +# +# Converts a name to the corresponding define, +# e.g. sys/stat.h becomes HAVE_SYS_STAT_H. +# +# Converts * to P and all non-alphanumeric to underscore. +# +proc feature-define-name {name {prefix HAVE_}} { + string toupper $prefix[regsub -all {[^a-zA-Z0-9]} [regsub -all {[*]} $name p] _] +} + +# If $file doesn't exist, or it's contents are different than $buf, +# the file is written and $script is executed. +# Otherwise a "file is unchanged" message is displayed. +proc write-if-changed {file buf {script {}}} { + set old [readfile $file ""] + if {$old eq $buf && [file exists $file]} { + msg-result "$file is unchanged" + } else { + writefile $file $buf\n + uplevel 1 $script + } +} + +# @make-template template ?outfile? +# +# Reads the input file /$template and writes the output file $outfile. +# If $outfile is blank/omitted, $template should end with ".in" which +# is removed to create the output file name. +# +# Each pattern of the form @define@ is replaced the the corresponding +# define, if it exists, or left unchanged if not. +# +# The special value @srcdir@ is subsituted with the relative +# path to the source directory from the directory where the output +# file is created. Use @top_srcdir@ for the absolute path. +# +# Conditional sections may be specified as follows: +## @if name == value +## lines +## @else +## lines +## @endif +# +# Where 'name' is a defined variable name and @else is optional. +# If the expression does not match, all lines through '@endif' are ignored. +# +# The alternative forms may also be used: +## @if name +## @if name != value +# +# Where the first form is true if the variable is defined, but not empty or 0 +# +# Currently these expressions can't be nested. +# +proc make-template {template {out {}}} { + set infile [file join $::autosetup(srcdir) $template] + + if {![file exists $infile]} { + user-error "Template $template is missing" + } + + # Define this as late as possible + define AUTODEPS $::autosetup(deps) + + if {$out eq ""} { + if {[file ext $template] ne ".in"} { + autosetup-error "make_template $template has no target file and can't guess" + } + set out [file rootname $template] + } + + set outdir [file dirname $out] + + # Make sure the directory exists + file mkdir $outdir + + # Set up srcdir to be relative to the target dir + define srcdir [relative-path [file join $::autosetup(srcdir) $outdir] $outdir] + + set mapping {} + foreach {n v} [array get ::define] { + lappend mapping @$n@ $v + } + set result {} + foreach line [split [readfile $infile] \n] { + if {[info exists cond]} { + set l [string trimright $line] + if {$l eq "@endif"} { + unset cond + continue + } + if {$l eq "@else"} { + set cond [expr {!$cond}] + continue + } + if {$cond} { + lappend result $line + } + continue + } + if {[regexp {^@if\s+(\w+)(.*)} $line -> name expression]} { + lassign $expression equal value + set varval [get-define $name ""] + if {$equal eq ""} { + set cond [expr {$varval ni {"" 0}}] + } else { + set cond [expr {$varval eq $value}] + if {$equal ne "=="} { + set cond [expr {!$cond}] + } + } + continue + } + lappend result $line + } + writefile $out [string map $mapping [join $result \n]]\n + + msg-result "Created [relative-path $out] from [relative-path $template]" +} + +# build/host tuples and cross-compilation prefix +set build [opt-val build] +define build_alias $build +if {$build eq ""} { + define build [config_guess] +} else { + define build [config_sub $build] +} + +set host [opt-val host] +define host_alias $host +if {$host eq ""} { + define host [get-define build] + set cross "" +} else { + define host [config_sub $host] + set cross $host- +} +define cross [get-env CROSS $cross] + +set prefix [opt-val prefix /usr/local] + +# These are for compatibility with autoconf +define target [get-define host] +define prefix $prefix +define builddir $autosetup(builddir) +define srcdir $autosetup(srcdir) +# Allow this to come from the environment +define top_srcdir [get-env top_srcdir [get-define srcdir]] + +# autoconf supports all of these +set exec_prefix [opt-val exec-prefix $prefix] +define exec_prefix $exec_prefix +foreach {name defpath} { + bindir /bin + sbindir /sbin + libexecdir /libexec + libdir /lib +} { + define $name [opt-val $name $exec_prefix$defpath] +} +foreach {name defpath} { + datadir /share + sysconfdir /etc + sharedstatedir /com + localstatedir /var + infodir /share/info + mandir /share/man + includedir /include +} { + define $name [opt-val $name $prefix$defpath] +} + +define SHELL [get-env SHELL [find-an-executable sh bash ksh]] + +# Windows vs. non-Windows +switch -glob -- [get-define host] { + *-*-ming* - *-*-cygwin { + define-feature windows + define EXEEXT .exe + } + default { + define EXEEXT "" + } +} + +# Display +msg-result "Host System...[get-define host]" +msg-result "Build System...[get-define build]" diff --git a/release/src/router/usbmodeswitch/jim/autosetup/test-tclsh b/release/src/router/usbmodeswitch/jim/autosetup/test-tclsh new file mode 100644 index 0000000000..75126d2444 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/autosetup/test-tclsh @@ -0,0 +1,20 @@ +# A small Tcl script to verify that the chosen +# interpreter works. Sometimes we might e.g. pick up +# an interpreter for a different arch. +# Outputs the full path to the interpreter + +if {[catch {info version} version] == 0} { + # This is Jim Tcl + if {$version >= 0.72} { + # Ensure that regexp works + regexp (a.*?) a + puts [info nameofexecutable] + exit 0 + } +} elseif {[catch {info tclversion} version] == 0} { + if {$version >= 8.5 && ![string match 8.5a* [info patchlevel]]} { + puts [info nameofexecutable] + exit 0 + } +} +exit 1 diff --git a/release/src/router/usbmodeswitch/jim/bootstrap.tcl b/release/src/router/usbmodeswitch/jim/bootstrap.tcl new file mode 100644 index 0000000000..e7adf4b637 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/bootstrap.tcl @@ -0,0 +1,3 @@ +# No need for package support in the bootstrap jimsh, but +# Tcl extensions call package require +proc package {args} {} diff --git a/release/src/router/usbmodeswitch/jim/configure b/release/src/router/usbmodeswitch/jim/configure new file mode 100755 index 0000000000..1c5586f4e9 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/configure @@ -0,0 +1,3 @@ +#!/bin/sh +dir="`dirname "$0"`/autosetup" +WRAPPER="$0" exec "`$dir/find-tclsh`" "$dir/autosetup" "$@" diff --git a/release/src/router/usbmodeswitch/jim/configure.ac b/release/src/router/usbmodeswitch/jim/configure.ac new file mode 100644 index 0000000000..b70b5d8a65 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/configure.ac @@ -0,0 +1 @@ +# Dummy configure.ac to make automake happy diff --git a/release/src/router/usbmodeswitch/jim/freebsd/andrew.txt b/release/src/router/usbmodeswitch/jim/freebsd/andrew.txt new file mode 100644 index 0000000000..8dbfcfc139 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/freebsd/andrew.txt @@ -0,0 +1,65 @@ +Delivered-To: oyvindharboe@gmail.com +Received: by 10.100.7.20 with SMTP id 20cs86142ang; + Wed, 16 Jul 2008 00:45:59 -0700 (PDT) +Received: by 10.142.238.12 with SMTP id l12mr5009290wfh.204.1216194359186; + Wed, 16 Jul 2008 00:45:59 -0700 (PDT) +Return-Path: +Received: from cpanel5.proisp.no (cpanel5.proisp.no [209.85.100.29]) + by mx.google.com with ESMTP id 31si6762736wff.16.2008.07.16.00.45.57; + Wed, 16 Jul 2008 00:45:59 -0700 (PDT) +Received-SPF: fail (google.com: domain of andrew@lunn.ch does not designate 209.85.100.29 as permitted sender) client-ip=209.85.100.29; +Authentication-Results: mx.google.com; spf=hardfail (google.com: domain of andrew@lunn.ch does not designate 209.85.100.29 as permitted sender) smtp.mail=andrew@lunn.ch +Received: from londo.lunn.ch ([80.238.139.98]:48839 ident=mail) + by cpanel5.proisp.no with esmtp (Exim 4.69) + (envelope-from ) + id 1KJ1ht-00085G-Ng + for oyvind.harboe@zylin.com; Wed, 16 Jul 2008 09:45:52 +0200 +Received: from lunn by londo.lunn.ch with local (Exim 3.36 #1 (Debian)) + id 1KJ1hq-0005ss-00; Wed, 16 Jul 2008 09:45:46 +0200 +Date: Wed, 16 Jul 2008 09:45:46 +0200 +From: Andrew Lunn +To: ?yvind Harboe +Cc: jim-devel@lists.berlios.de, antirez@gmail.com, patthoyts@users.sf.net, + andrew@lunn.ch, openocd@duaneellis.com, uklein@klein-messgeraete.de, + ml-jim@qiao.in-berlin.de +Subject: Re: Change Jim Tcl license +Message-ID: <20080716074546.GC24771@lunn.ch> +References: +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Disposition: inline +In-Reply-To: +User-Agent: Mutt/1.5.18 (2008-05-17) +X-Spam-Status: No, score=-2.6 +X-Spam-Score: -25 +X-Spam-Bar: -- +X-Spam-Flag: NO +X-AntiAbuse: This header was added to track abuse, please include it with any abuse report +X-AntiAbuse: Primary Hostname - cpanel5.proisp.no +X-AntiAbuse: Original Domain - zylin.com +X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] +X-AntiAbuse: Sender Address Domain - lunn.ch +X-Source: +X-Source-Args: +X-Source-Dir: + +On Wed, Jul 16, 2008 at 09:34:14AM +0200, ?yvind Harboe wrote: +> Hi all, +> +> I'm currently the maintainer of Jim Tcl trying as best as I can +> to fill Salvatore's shoes. +> +> Short story: +> +> If you have contributed to Jim Tcl, please reply to this email +> that you agree that we can switch Jim Tcl to a FreeBSD license. + +I've no problems with this, but my contributions are very minimal. + +Do you want this written down, in blood, to keep the lawyers happy? + +At a minimum i think everybody's agreement needs to be posted to a +public email list which is publicly archived etc so there is a +record of the agreement... + + Andrew diff --git a/release/src/router/usbmodeswitch/jim/freebsd/clemens.txt b/release/src/router/usbmodeswitch/jim/freebsd/clemens.txt new file mode 100644 index 0000000000..806f7ed93d --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/freebsd/clemens.txt @@ -0,0 +1,87 @@ + +Delivered-To: oyvindharboe@gmail.com +Received: by 10.100.7.20 with SMTP id 20cs114742ang; + Wed, 16 Jul 2008 08:58:18 -0700 (PDT) +Received: by 10.114.137.2 with SMTP id k2mr325372wad.95.1216223896673; + Wed, 16 Jul 2008 08:58:16 -0700 (PDT) +Return-Path: +Received: from cpanel5.proisp.no (cpanel5.proisp.no [209.85.100.29]) + by mx.google.com with ESMTP id m28si10145125waf.16.2008.07.16.08.58.15; + Wed, 16 Jul 2008 08:58:16 -0700 (PDT) +Received-SPF: neutral (google.com: 209.85.100.29 is neither permitted nor denied by best guess record for domain of ml-jim@qiao.in-berlin.de) client-ip=209.85.100.29; +Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.100.29 is neither permitted nor denied by best guess record for domain of ml-jim@qiao.in-berlin.de) smtp.mail=ml-jim@qiao.in-berlin.de +Received: from gnu.in-berlin.de ([192.109.42.4]:58401) + by cpanel5.proisp.no with esmtps (TLSv1:AES256-SHA:256) + (Exim 4.69) + (envelope-from ) + id 1KJ9OG-0006Hf-8y + for oyvind.harboe@zylin.com; Wed, 16 Jul 2008 17:58:07 +0200 +X-Envelope-From: ml-jim@qiao.in-berlin.de +X-Envelope-To: +Received: from qiao.in-berlin.de (qiao.in-berlin.de [217.197.85.72]) + by gnu.in-berlin.de (8.13.8/8.13.8/Debian-2) with ESMTP id m6GFvxio009504 + for ; Wed, 16 Jul 2008 17:58:02 +0200 +Received: from [192.168.0.10] ([::ffff:192.168.0.10]) + by qiao.in-berlin.de with esmtp; Wed, 16 Jul 2008 18:00:04 +0200 + id 0001D68D.487E1B04.000042E7 +In-Reply-To: +References: +Mime-Version: 1.0 (Apple Message framework v753.1) +Content-Type: text/plain; charset=ISO-8859-1; delsp=yes; format=flowed +Message-Id: +Cc: jim-devel@lists.berlios.de, antirez@gmail.com, patthoyts@users.sf.net, + andrew@lunn.ch, openocd@duaneellis.com, uklein@klein-messgeraete.de +Content-Transfer-Encoding: quoted-printable +From: Clemens Hintze +Subject: Re: Change Jim Tcl license +Date: Wed, 16 Jul 2008 17:58:14 +0200 +To: "=?ISO-8859-1?Q?\"=D8yvind_Harboe\"?=" +X-Mailer: Apple Mail (2.753.1) +X-Spam-Score: (0.101) BAYES_50,RDNS_NONE +X-Scanned-By: MIMEDefang_at_IN-Berlin_e.V. on 192.109.42.4 +X-Spam-Status: No, score=-2.6 +X-Spam-Score: -25 +X-Spam-Bar: -- +X-Spam-Flag: NO +X-AntiAbuse: This header was added to track abuse, please include it with any abuse report +X-AntiAbuse: Primary Hostname - cpanel5.proisp.no +X-AntiAbuse: Original Domain - zylin.com +X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] +X-AntiAbuse: Sender Address Domain - qiao.in-berlin.de +X-Source: +X-Source-Args: +X-Source-Dir: + + +Am 16.07.2008 um 09:34 schrieb =D8yvind Harboe: + +> Hi all, + +Hi =D8yvind, + +(...) + +> If you have contributed to Jim Tcl, please reply to this email +> that you agree that we can switch Jim Tcl to a FreeBSD license. +> +> Once I have a record of all contributors agreeing to switch +> to a FreeBSD license, I'll update CVS. + +No problem with me: I agree to permit my contributions to the Jim =20 +project to be +re-licensed under a BSD compatible license. + +(...) + +> Please let me know if any of the emails below are wrong(chi is +> missing) or the list is not complete. + +After consultation with the voices in my head, I can ensure you, =20 +'chi' is also agreeing with the re-licensing, because its me too ;-) + +Thank you very much to revive Jim! :-) + +Best regards, +Clemens Hintze. + +(...)= diff --git a/release/src/router/usbmodeswitch/jim/freebsd/duane.txt b/release/src/router/usbmodeswitch/jim/freebsd/duane.txt new file mode 100644 index 0000000000..56f962cb85 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/freebsd/duane.txt @@ -0,0 +1,65 @@ +Delivered-To: oyvindharboe@gmail.com +Received: by 10.100.7.20 with SMTP id 20cs93801ang; + Wed, 16 Jul 2008 03:40:02 -0700 (PDT) +Received: by 10.142.148.10 with SMTP id v10mr5070849wfd.317.1216204801306; + Wed, 16 Jul 2008 03:40:01 -0700 (PDT) +Return-Path: +Received: from cpanel5.proisp.no (cpanel5.proisp.no [209.85.100.29]) + by mx.google.com with ESMTP id 27si9313433wff.3.2008.07.16.03.40.00; + Wed, 16 Jul 2008 03:40:01 -0700 (PDT) +Received-SPF: neutral (google.com: 209.85.100.29 is neither permitted nor denied by best guess record for domain of openocd@duaneellis.com) client-ip=209.85.100.29; +Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.100.29 is neither permitted nor denied by best guess record for domain of openocd@duaneellis.com) smtp.mail=openocd@duaneellis.com +Received: from smtpout10-04.prod.mesa1.secureserver.net ([64.202.165.238]:48803 helo=smtpout10.prod.mesa1.secureserver.net) + by cpanel5.proisp.no with smtp (Exim 4.69) + (envelope-from ) + id 1KJ4QL-0005cq-GB + for oyvind.harboe@zylin.com; Wed, 16 Jul 2008 12:39:54 +0200 +Received: (qmail 2305 invoked from network); 16 Jul 2008 10:39:56 -0000 +Received: from unknown (68.37.53.103) + by smtpout10-04.prod.mesa1.secureserver.net (64.202.165.238) with ESMTP; 16 Jul 2008 10:39:55 -0000 +Message-ID: <487DCFEC.4010104@duaneellis.com> +Date: Wed, 16 Jul 2008 06:39:40 -0400 +From: Duane Ellis +Reply-To: openocd@duaneellis.com +User-Agent: Thunderbird 2.0.0.14 (Windows/20080421) +MIME-Version: 1.0 +To: =?ISO-8859-1?Q?=D8yvind_Harboe?= +CC: jim-devel@lists.berlios.de, antirez@gmail.com, + patthoyts@users.sf.net, andrew@lunn.ch, uklein@klein-messgeraete.de, + ml-jim@qiao.in-berlin.de +Subject: Re: Change Jim Tcl license +References: +In-Reply-To: +Content-Type: text/plain; charset=ISO-8859-1; format=flowed +Content-Transfer-Encoding: 8bit +X-Spam-Status: No, score=-2.6 +X-Spam-Score: -25 +X-Spam-Bar: -- +X-Spam-Flag: NO +X-AntiAbuse: This header was added to track abuse, please include it with any abuse report +X-AntiAbuse: Primary Hostname - cpanel5.proisp.no +X-AntiAbuse: Original Domain - zylin.com +X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] +X-AntiAbuse: Sender Address Domain - duaneellis.com +X-Source: +X-Source-Args: +X-Source-Dir: + +Oyvind Harboe wrote: +> Short story: +> +> If you have contributed to Jim Tcl, please reply to this email +> that you agree that we can switch Jim Tcl to a FreeBSD license. +> +> Once I have a record of all contributors agreeing to switch +> to a FreeBSD license, I'll update CVS. +> +> +OK - from me + + --Duane. + +-Duane. + + + diff --git a/release/src/router/usbmodeswitch/jim/freebsd/oharboe.txt b/release/src/router/usbmodeswitch/jim/freebsd/oharboe.txt new file mode 100644 index 0000000000..af4a8e8478 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/freebsd/oharboe.txt @@ -0,0 +1,85 @@ + +Received: by 10.100.7.20 with HTTP; Wed, 16 Jul 2008 10:12:05 -0700 (PDT) +Message-ID: +Date: Wed, 16 Jul 2008 19:12:05 +0200 +From: "=?ISO-8859-1?Q?=D8yvind_Harboe?=" +Sender: oyvindharboe@gmail.com +To: jim-devel@lists.berlios.de +Subject: Re: Change Jim Tcl license +In-Reply-To: +MIME-Version: 1.0 +Content-Type: text/plain; charset=ISO-8859-1 +Content-Transfer-Encoding: quoted-printable +Content-Disposition: inline +References: +Delivered-To: oyvindharboe@gmail.com +X-Google-Sender-Auth: fc18e85532eee8f2 + +For the record: + +I would like my contributions to Jim Tcl to be under a FreeBSD license too= +. + +On Wed, Jul 16, 2008 at 9:34 AM, =D8yvind Harboe = +wrote: +> Hi all, +> +> I'm currently the maintainer of Jim Tcl trying as best as I can +> to fill Salvatore's shoes. +> +> Short story: +> +> If you have contributed to Jim Tcl, please reply to this email +> that you agree that we can switch Jim Tcl to a FreeBSD license. +> +> Once I have a record of all contributors agreeing to switch +> to a FreeBSD license, I'll update CVS. +> +> Long story: +> +> The current Jim Tcl license has a problem with GPL. If you +> link GPL code and Jim Tcl, the result is no license at all. +> +> This prevents Jim Tcl from being used in GPL projects. +> +> Lately Jim Tcl has been used with OpenOCD, a GPL project, +> and the license issue must be resolved one way or another. +> +> Upon conferring with Jonathan Larmour , who +> has kindly helped out with his knowledge on the topic, I have +> concluded that the best way to rectify this is to change the +> Jim Tcl license to a FreeBSD license. See OpenOCD mailing +> list for a discussion on this if you want details. +> +> http://www.fsf.org/licensing/licenses/index_html#FreeBSD +> +> As far as I can determine, below is the complete list of contributors. +> +> +> antirez - Salvatore Sanfilippo +> patthoyts - ?? Pat Thoyts +> oharboe - =D8yvind Harboe - soyvind.harboe@zylin.com +> chi - ?? +> Andrew Lunn +> Duane Ellis +> Uwe Klein +> Clemens Hintze ml-jim@qiao.in-berlin.de +> +> Please let me know if any of the emails below are wrong(chi is +> missing) or the list is not complete. +> +> +> -- +> =D8yvind Harboe +> http://www.zylin.com/zy1000.html +> ARM7 ARM9 XScale Cortex +> JTAG debugger and flash programmer +> + + + +--=20 +=D8yvind Harboe +http://www.zylin.com/zy1000.html +ARM7 ARM9 XScale Cortex +JTAG debugger and flash programmer diff --git a/release/src/router/usbmodeswitch/jim/freebsd/pat.txt b/release/src/router/usbmodeswitch/jim/freebsd/pat.txt new file mode 100644 index 0000000000..988a599f4d --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/freebsd/pat.txt @@ -0,0 +1,84 @@ + +Delivered-To: oyvindharboe@gmail.com +Received: by 10.100.7.20 with SMTP id 20cs108097ang; + Wed, 16 Jul 2008 07:49:02 -0700 (PDT) +Received: by 10.142.232.20 with SMTP id e20mr80874wfh.138.1216219741865; + Wed, 16 Jul 2008 07:49:01 -0700 (PDT) +Return-Path: +Received: from cpanel5.proisp.no (cpanel5.proisp.no [209.85.100.29]) + by mx.google.com with ESMTP id 30si10551683wff.18.2008.07.16.07.49.01; + Wed, 16 Jul 2008 07:49:01 -0700 (PDT) +Received-SPF: neutral (google.com: 209.85.100.29 is neither permitted nor denied by best guess record for domain of patthoyts@users.sourceforge.net) client-ip=209.85.100.29; +Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.100.29 is neither permitted nor denied by best guess record for domain of patthoyts@users.sourceforge.net) smtp.mail=patthoyts@users.sourceforge.net +Received: from smtp-out4.blueyonder.co.uk ([195.188.213.7]:38596) + by cpanel5.proisp.no with esmtp (Exim 4.69) + (envelope-from ) + id 1KJ8JH-0000Vd-OT + for oyvind.harboe@zylin.com; Wed, 16 Jul 2008 16:48:52 +0200 +Received: from [172.23.170.141] (helo=anti-virus02-08) + by smtp-out4.blueyonder.co.uk with smtp (Exim 4.52) + id 1KJ8JO-0007r0-Cy; Wed, 16 Jul 2008 15:48:58 +0100 +Received: from [77.102.249.21] (helo=badger.patthoyts.tk) + by asmtp-out4.blueyonder.co.uk with esmtp (Exim 4.52) + id 1KJ8J6-0000gY-VY; Wed, 16 Jul 2008 15:48:41 +0100 +Received: by badger.patthoyts.tk (Postfix, from userid 1000) + id 810535184F; Wed, 16 Jul 2008 15:48:40 +0100 (BST) +Sender: pat@badger.patthoyts.tk +To: =?iso-8859-1?q?=D8yvind_Harboe?= +Cc: jim-devel@lists.berlios.de +Subject: Re: Change Jim Tcl license +References: +X-Face: .`d#euqz@6H{";Ysmx2IVe_7M3vA+2w1X[QLk?ZO&QRauXQL{*L'$3getx}9+zK.-KWDx3. + qrlR)76MFb`6bgoGvLpLtcQKB=X~;* +Date: 16 Jul 2008 15:48:39 +0100 +In-Reply-To: +Message-ID: <87fxq97um0.fsf@badger.patthoyts.tk> +Lines: 27 +User-Agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.3 +MIME-Version: 1.0 +Content-Type: text/plain; charset=iso-8859-1 +Content-Transfer-Encoding: quoted-printable +X-Spam-Status: No, score=-2.6 +X-Spam-Score: -25 +X-Spam-Bar: -- +X-Spam-Flag: NO +X-AntiAbuse: This header was added to track abuse, please include it with any abuse report +X-AntiAbuse: Primary Hostname - cpanel5.proisp.no +X-AntiAbuse: Original Domain - zylin.com +X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] +X-AntiAbuse: Sender Address Domain - users.sourceforge.net +X-Source: +X-Source-Args: +X-Source-Dir: + +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +"=D8yvind Harboe" writes: + +>If you have contributed to Jim Tcl, please reply to this email +>that you agree that we can switch Jim Tcl to a FreeBSD license. +> +>Once I have a record of all contributors agreeing to switch +>to a FreeBSD license, I'll update CVS. + +I hereby agree to permit my contributions to the Jim project to be +re-licensed under a BSD compatible license. + +- --=20 +Pat Thoyts http://www.patthoyts.tk/ +PGP fingerprint 2C 6E 98 07 2C 59 C8 97 10 CE 11 E6 04 E0 B9 DD +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.8 (SunOS) +Comment: Processed by Mailcrypt 3.5.8 + +iQCVAwUBSH4KO2B90JXwhOSJAQKtqQP9ERwSXpbP69l4JSrunG29Rhu2F3r83zu3 +GAKpFu4HwkVnIStLQ4o3tsqG9uKrVDbRMa187eSwHmlXXIMwDlkCKNsDFxvdLDZz +kbTYDibspYSw6CjwOUSTXifK9P7ho4Q7PtsRnJ8T1IMlGJlwg39Rxd+mpEO/if3q +ExIwM1aBbAs=3D +=3Du8si +-----END PGP SIGNATURE----- + diff --git a/release/src/router/usbmodeswitch/jim/freebsd/salvatore.txt b/release/src/router/usbmodeswitch/jim/freebsd/salvatore.txt new file mode 100644 index 0000000000..5bbf973b75 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/freebsd/salvatore.txt @@ -0,0 +1,88 @@ + +Delivered-To: oyvindharboe@gmail.com +Received: by 10.100.7.20 with SMTP id 20cs113143ang; + Wed, 16 Jul 2008 08:41:11 -0700 (PDT) +Received: by 10.142.140.15 with SMTP id n15mr127048wfd.84.1216222870242; + Wed, 16 Jul 2008 08:41:10 -0700 (PDT) +Return-Path: +Received: from cpanel5.proisp.no (cpanel5.proisp.no [209.85.100.29]) + by mx.google.com with ESMTP id 29si7397124wfg.0.2008.07.16.08.41.08; + Wed, 16 Jul 2008 08:41:10 -0700 (PDT) +Received-SPF: neutral (google.com: 209.85.100.29 is neither permitted nor denied by domain of antirez@gmail.com) client-ip=209.85.100.29; +Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.100.29 is neither permitted nor denied by domain of antirez@gmail.com) smtp.mail=antirez@gmail.com; dkim=pass (test mode) header.i=@gmail.com +Received: from fg-out-1718.google.com ([72.14.220.155]:16058) + by cpanel5.proisp.no with esmtp (Exim 4.69) + (envelope-from ) + id 1KJ97g-0004yX-1W + for oyvind.harboe@zylin.com; Wed, 16 Jul 2008 17:40:59 +0200 +Received: by fg-out-1718.google.com with SMTP id l27so3985052fgb.19 + for ; Wed, 16 Jul 2008 08:40:59 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=gamma; + h=domainkey-signature:received:received:message-id:date:from:to + :subject:cc:in-reply-to:mime-version:content-type + :content-transfer-encoding:content-disposition:references; + bh=/aWDZQfgMBPqomYWZ2AUKOhhGMju+bwnSBbKL8MBonA=; + b=i0P3OKDopn/vHfa5ZrUvBjuPBnj43GMw8FOXKjxM/IfvywJParYqBS2Vmlw8RTndFg + J5wwxXf5056cZu/GbKbj8xLfylFfSInVaO7OnDutA3CeX1iU35my1DU6l9W6ILkLiT1P + Azi3L27rFQrzau/s53VU/UVELc3WckWdu1a1k= +DomainKey-Signature: a=rsa-sha1; c=nofws; + d=gmail.com; s=gamma; + h=message-id:date:from:to:subject:cc:in-reply-to:mime-version + :content-type:content-transfer-encoding:content-disposition + :references; + b=ww2MIz9svJttgS8mTRBhEX8Isveugn2hl3sMcgh0hZ1+ln8YbiysxYxZkdddewWm02 + WXsWgSgwy7MIPAUK1tNjzgkZ2l789SdrAtBCmqmRWJJI+ESTqbHMz8cqW+QRVP/A9Dfm + 8+AR85DHi7SOB0mdHtq9fsavZReUdaSIgy6F4= +Received: by 10.86.80.5 with SMTP id d5mr2284433fgb.19.1216222858224; + Wed, 16 Jul 2008 08:40:58 -0700 (PDT) +Received: by 10.86.50.18 with HTTP; Wed, 16 Jul 2008 08:40:58 -0700 (PDT) +Message-ID: +Date: Wed, 16 Jul 2008 17:40:58 +0200 +From: "Salvatore Sanfilippo" +To: "=?ISO-8859-1?Q?=D8yvind_Harboe?=" +Subject: Re: Change Jim Tcl license +Cc: jim-devel@lists.berlios.de, patthoyts@users.sf.net, andrew@lunn.ch, + openocd@duaneellis.com, uklein@klein-messgeraete.de, + ml-jim@qiao.in-berlin.de +In-Reply-To: +MIME-Version: 1.0 +Content-Type: text/plain; charset=ISO-8859-1 +Content-Transfer-Encoding: 7bit +Content-Disposition: inline +References: +X-Spam-Status: No, score=-2.6 +X-Spam-Score: -25 +X-Spam-Bar: -- +X-Spam-Flag: NO +X-AntiAbuse: This header was added to track abuse, please include it with any abuse report +X-AntiAbuse: Primary Hostname - cpanel5.proisp.no +X-AntiAbuse: Original Domain - zylin.com +X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] +X-AntiAbuse: Sender Address Domain - gmail.com +X-Source: +X-Source-Args: +X-Source-Dir: + +I agree to permit my contributions to the Jim project to be +re-licensed under a BSD compatible license. + +Since I'm currently the top contributor if it's safer from +the legal point of view I can also put a tar.gz of the current +Jim source code with a BSD "LICENSE" file on my website. + +Otherwise I can sign by hand a letter and send a digitalized +image here. + +Ciao, +Salvatore + +-- +Salvatore 'antirez' Sanfilippo +http://antirez.com + +Organizations which design systems are constrained to produce designs +which are copies of the communication structures of these +organizations. + +Conway's Law diff --git a/release/src/router/usbmodeswitch/jim/freebsd/uwe.txt b/release/src/router/usbmodeswitch/jim/freebsd/uwe.txt new file mode 100644 index 0000000000..035ca32f20 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/freebsd/uwe.txt @@ -0,0 +1,73 @@ +Delivered-To: oyvindharboe@gmail.com +Received: by 10.100.7.20 with SMTP id 20cs89014ang; + Wed, 16 Jul 2008 01:58:32 -0700 (PDT) +Received: by 10.142.125.9 with SMTP id x9mr5028534wfc.123.1216198711465; + Wed, 16 Jul 2008 01:58:31 -0700 (PDT) +Return-Path: +Received: from cpanel5.proisp.no (cpanel5.proisp.no [209.85.100.29]) + by mx.google.com with ESMTP id 30si6756166wfa.10.2008.07.16.01.58.29; + Wed, 16 Jul 2008 01:58:31 -0700 (PDT) +Received-SPF: neutral (google.com: 209.85.100.29 is neither permitted nor denied by domain of wiederling@googlemail.com) client-ip=209.85.100.29; +Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.100.29 is neither permitted nor denied by domain of wiederling@googlemail.com) smtp.mail=wiederling@googlemail.com; dkim=pass (test mode) header.i=@googlemail.com +Received: from wr-out-0506.google.com ([64.233.184.233]:51225) + by cpanel5.proisp.no with esmtp (Exim 4.69) + (envelope-from ) + id 1KJ2q7-00057b-IR + for oyvind.harboe@zylin.com; Wed, 16 Jul 2008 10:58:24 +0200 +Received: by wr-out-0506.google.com with SMTP id c8so2209154wra.27 + for ; Wed, 16 Jul 2008 01:58:25 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=googlemail.com; s=gamma; + h=domainkey-signature:received:received:message-id:date:from:to + :subject:cc:in-reply-to:mime-version:content-type + :content-transfer-encoding:content-disposition:references; + bh=VxcH0g2H5iLUo27gqJiqrlY4uVbN1NFE4skyMKqysPM=; + b=JPK53r6LQ6GqBCG1kfVYyTPuPuVhlBrbzQ8oSBwpwuwwB7t3CSv+c75jRjb/n3y8mi + gN1r6noZucK9ZpRZiHxYZpHVhYFcWbZ+ZXM75H2qIFfl4YDzfgg/Ub7CzoR2LskuBsRk + DMH2LnyAYf+Om2YAKJdkoMnGbPMDMFSrNHeIc= +DomainKey-Signature: a=rsa-sha1; c=nofws; + d=googlemail.com; s=gamma; + h=message-id:date:from:to:subject:cc:in-reply-to:mime-version + :content-type:content-transfer-encoding:content-disposition + :references; + b=VAGlxpb1YGbex/eaS0tQgWvH/lWHzgD5R/rxjshVSwZJOStwqMA1F5jNQgybQFIn1F + zWoiAV81uWMzBEGYab7SGsStWLxovcBSgi9NL+XqwAkhBdrWjgFPvpBHn5PvgOOXEhGH + EGhjrY8qp2LSxhFcW3/DvgObhBBKtY1J+qzvA= +Received: by 10.90.115.17 with SMTP id n17mr1231758agc.90.1216198705850; + Wed, 16 Jul 2008 01:58:25 -0700 (PDT) +Received: by 10.90.105.18 with HTTP; Wed, 16 Jul 2008 01:58:25 -0700 (PDT) +Message-ID: <1af31b6f0807160158o295303adh43abdd34fbe8ec99@mail.gmail.com> +Date: Wed, 16 Jul 2008 10:58:25 +0200 +From: "Uwe Klein" +To: "=?ISO-8859-1?Q?=D8yvind_Harboe?=" +Subject: Re: [Jim-devel] Change Jim Tcl license +Cc: jim-devel@lists.berlios.de, patthoyts@users.sf.net, andrew@lunn.ch, + uklein@klein-messgeraete.de, antirez@gmail.com, + openocd@duaneellis.com, ml-jim@qiao.in-berlin.de +In-Reply-To: +MIME-Version: 1.0 +Content-Type: text/plain; charset=ISO-8859-1 +Content-Transfer-Encoding: 7bit +Content-Disposition: inline +References: +X-Spam-Status: No, score=-2.6 +X-Spam-Score: -25 +X-Spam-Bar: -- +X-Spam-Flag: NO +X-AntiAbuse: This header was added to track abuse, please include it with any abuse report +X-AntiAbuse: Primary Hostname - cpanel5.proisp.no +X-AntiAbuse: Original Domain - zylin.com +X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] +X-AntiAbuse: Sender Address Domain - googlemail.com +X-Source: +X-Source-Args: +X-Source-Dir: + +> If you have contributed to Jim Tcl, please reply to this email +> that you agree that we can switch Jim Tcl to a FreeBSD license. + +For Uwe Klein + +This is OK with me. + +uwe diff --git a/release/src/router/usbmodeswitch/jim/glob.tcl b/release/src/router/usbmodeswitch/jim/glob.tcl new file mode 100644 index 0000000000..cd94d8dc08 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/glob.tcl @@ -0,0 +1,129 @@ +# Implements a Tcl-compatible glob command based on readdir +# +# (c) 2008 Steve Bennett +# +# See LICENCE in this directory for licensing. + +package require readdir + +# Implements the Tcl glob command +# +# Usage: glob ?-nocomplain? pattern ... +# +# Patterns use 'string match' (glob) pattern matching for each +# directory level, plus support for braced alternations. +# +# e.g. glob "te[a-e]*/*.{c,tcl}" +# +# Note: files starting with . will only be returned if matching component +# of the pattern starts with . +proc glob {args} { + + # If $dir is a directory, return a list of all entries + # it contains which match $pattern + # + local proc glob.readdir_pattern {dir pattern} { + set result {} + + # readdir doesn't return . or .., so simulate it here + if {$pattern in {. ..}} { + return $pattern + } + + # If the pattern isn't actually a pattern... + if {[string match {*[*?]*} $pattern]} { + # Use -nocomplain here to return nothing if $dir is not a directory + set files [readdir -nocomplain $dir] + } elseif {[file isdir $dir] && [file exists $dir/$pattern]} { + set files [list $pattern] + } else { + set files "" + } + + foreach name $files { + if {[string match $pattern $name]} { + # Only include entries starting with . if the pattern starts with . + if {[string index $name 0] eq "." && [string index $pattern 0] ne "."} { + continue + } + lappend result $name + } + } + + return $result + } + + # If the pattern contains a braced expression, return a list of + # patterns with the braces expanded. {c,b}* => c* b* + # Otherwise just return the pattern + # Note: Only supports one braced expression. i.e. not {a,b}*{c,d}* + proc glob.expandbraces {pattern} { + # Avoid regexp for dependency reasons. + # XXX: Doesn't handle backslashed braces + if {[set fb [string first "\{" $pattern]] < 0} { + return $pattern + } + if {[set nb [string first "\}" $pattern $fb]] < 0} { + return $pattern + } + set before [string range $pattern 0 $fb-1] + set braced [string range $pattern $fb+1 $nb-1] + set after [string range $pattern $nb+1 end] + + lmap part [split $braced ,] { + set pat $before$part$after + } + } + + # Core glob implementation. Returns a list of files/directories matching the pattern + proc glob.glob {pattern} { + set dir [file dirname $pattern] + if {$dir eq $pattern} { + # At the top level + return [list $dir] + } + + # Recursively expand the parent directory + set dirlist [glob.glob $dir] + set pattern [file tail $pattern] + + # Now collect the fiels/directories + set result {} + foreach dir $dirlist { + set globdir $dir + if {[string match "*/" $dir]} { + set sep "" + } elseif {$dir eq "."} { + set globdir "" + set sep "" + } else { + set sep / + } + foreach pat [glob.expandbraces $pattern] { + foreach name [glob.readdir_pattern $dir $pat] { + lappend result $globdir$sep$name + } + } + } + return $result + } + + # Start of main glob + set nocomplain 0 + + if {[lindex $args 0] eq "-nocomplain"} { + set nocomplain 1 + set args [lrange $args 1 end] + } + + set result {} + foreach pattern $args { + lappend result {*}[glob.glob $pattern] + } + + if {$nocomplain == 0 && [llength $result] == 0} { + return -code error "no files matched glob patterns" + } + + return $result +} diff --git a/release/src/router/usbmodeswitch/jim/initjimsh.tcl b/release/src/router/usbmodeswitch/jim/initjimsh.tcl new file mode 100644 index 0000000000..a764f3a791 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/initjimsh.tcl @@ -0,0 +1,27 @@ +# This pseudo-package is loaded from jimsh to add additional +# paths to $auto_path and to source ~/.jimrc + +proc _jimsh_init {} { + rename _jimsh_init {} + + # Add to the standard auto_path + lappend p {*}[split [env JIMLIB {}] $::tcl_platform(pathSeparator)] + lappend p {*}$::auto_path + lappend p [file dirname [info nameofexecutable]] + set ::auto_path $p + + if {$::tcl_interactive && [env HOME {}] ne ""} { + foreach src {.jimrc jimrc.tcl} { + if {[file exists [env HOME]/$src]} { + uplevel #0 source [env HOME]/$src + break + } + } + } +} + +if {$tcl_platform(platform) eq "windows"} { + set jim_argv0 [string map {\\ /} $jim_argv0] +} + +_jimsh_init diff --git a/release/src/router/usbmodeswitch/jim/jim-aio.c b/release/src/router/usbmodeswitch/jim/jim-aio.c new file mode 100644 index 0000000000..710a47d362 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-aio.c @@ -0,0 +1,1353 @@ + +/* Jim - A small embeddable Tcl interpreter + * + * Copyright 2005 Salvatore Sanfilippo + * Copyright 2005 Clemens Hintze + * Copyright 2005 patthoyts - Pat Thoyts + * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com + * Copyright 2008 Andrew Lunn + * Copyright 2008 Duane Ellis + * Copyright 2008 Uwe Klein + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + **/ + +#include +#include +#include +#include +#include + +#include "jim.h" +#include "jimautoconf.h" + +#if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H) +#include +#include +#include +#include +#ifdef HAVE_SYS_UN_H +#include +#endif +#else +#define JIM_ANSIC +#endif + +#include "jim-eventloop.h" +#include "jim-subcmd.h" + +#define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */ +#define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */ + +#define AIO_KEEPOPEN 1 + +#if defined(JIM_IPV6) +#define IPV6 1 +#else +#define IPV6 0 +#ifndef PF_INET6 +#define PF_INET6 0 +#endif +#endif + +#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP) +union sockaddr_any { + struct sockaddr sa; + struct sockaddr_in sin; +#if IPV6 + struct sockaddr_in6 sin6; +#endif +}; + +#ifndef HAVE_INET_NTOP +const char *inet_ntop(int af, const void *src, char *dst, int size) +{ + if (af != PF_INET) { + return NULL; + } + snprintf(dst, size, "%s", inet_ntoa(((struct sockaddr_in *)src)->sin_addr)); + return dst; +} +#endif +#endif /* JIM_BOOTSTRAP */ + +typedef struct AioFile +{ + FILE *fp; + Jim_Obj *filename; + int type; + int OpenFlags; /* AIO_KEEPOPEN? keep FILE* */ + int fd; +#ifdef O_NDELAY + int flags; +#endif + Jim_Obj *rEvent; + Jim_Obj *wEvent; + Jim_Obj *eEvent; + int addr_family; +} AioFile; + +static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv); +static int JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename, + const char *hdlfmt, int family, const char *mode); + +#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP) +static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen) +{ +#if IPV6 + /* + * An IPv6 addr/port looks like: + * [::1] + * [::1]:2000 + * [fe80::223:6cff:fe95:bdc0%en1]:2000 + * [::]:2000 + * 2000 + * + * Note that the "any" address is ::, which is the same as when no address is specified. + */ + char *sthost = NULL; + const char *stport; + int ret = JIM_OK; + struct addrinfo req; + struct addrinfo *ai; + + stport = strrchr(hostport, ':'); + if (!stport) { + /* No : so, the whole thing is the port */ + stport = hostport; + hostport = "::"; + sthost = Jim_StrDup(hostport); + } + else { + stport++; + } + + if (*hostport == '[') { + /* This is a numeric ipv6 address */ + char *pt = strchr(++hostport, ']'); + if (pt) { + sthost = Jim_StrDupLen(hostport, pt - hostport); + } + } + + if (!sthost) { + sthost = Jim_StrDupLen(hostport, stport - hostport - 1); + } + + memset(&req, '\0', sizeof(req)); + req.ai_family = PF_INET6; + + if (getaddrinfo(sthost, NULL, &req, &ai)) { + Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport); + ret = JIM_ERR; + } + else { + memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen); + *salen = ai->ai_addrlen; + + sa->sin.sin_port = htons(atoi(stport)); + + freeaddrinfo(ai); + } + Jim_Free(sthost); + + return ret; +#else + Jim_SetResultString(interp, "ipv6 not supported", -1); + return JIM_ERR; +#endif +} + +static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen) +{ + /* An IPv4 addr/port looks like: + * 192.168.1.5 + * 192.168.1.5:2000 + * 2000 + * + * If the address is missing, INADDR_ANY is used. + * If the port is missing, 0 is used (only useful for server sockets). + */ + char *sthost = NULL; + const char *stport; + int ret = JIM_OK; + + stport = strrchr(hostport, ':'); + if (!stport) { + /* No : so, the whole thing is the port */ + stport = hostport; + sthost = Jim_StrDup("0.0.0.0"); + } + else { + sthost = Jim_StrDupLen(hostport, stport - hostport); + stport++; + } + + { +#ifdef HAVE_GETADDRINFO + struct addrinfo req; + struct addrinfo *ai; + memset(&req, '\0', sizeof(req)); + req.ai_family = PF_INET; + + if (getaddrinfo(sthost, NULL, &req, &ai)) { + ret = JIM_ERR; + } + else { + memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen); + *salen = ai->ai_addrlen; + freeaddrinfo(ai); + } +#else + struct hostent *he; + + ret = JIM_ERR; + + if ((he = gethostbyname(sthost)) != NULL) { + if (he->h_length == sizeof(sa->sin.sin_addr)) { + *salen = sizeof(sa->sin); + sa->sin.sin_family= he->h_addrtype; + memcpy(&sa->sin.sin_addr, he->h_addr, he->h_length); /* set address */ + ret = JIM_OK; + } + } +#endif + + sa->sin.sin_port = htons(atoi(stport)); + } + Jim_Free(sthost); + + if (ret != JIM_OK) { + Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport); + } + + return ret; +} + +#ifdef HAVE_SYS_UN_H +static int JimParseDomainAddress(Jim_Interp *interp, const char *path, struct sockaddr_un *sa) +{ + sa->sun_family = PF_UNIX; + snprintf(sa->sun_path, sizeof(sa->sun_path), "%s", path); + + return JIM_OK; +} +#endif +#endif /* JIM_BOOTSTRAP */ + +static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name) +{ + if (name) { + Jim_SetResultFormatted(interp, "%#s: %s", name, strerror(errno)); + } + else { + Jim_SetResultString(interp, strerror(errno), -1); + } +} + +static void JimAioDelProc(Jim_Interp *interp, void *privData) +{ + AioFile *af = privData; + + JIM_NOTUSED(interp); + + Jim_DecrRefCount(interp, af->filename); + + if (!(af->OpenFlags & AIO_KEEPOPEN)) { + fclose(af->fp); + } +#ifdef jim_ext_eventloop + /* remove existing EventHandlers */ + if (af->rEvent) { + Jim_DeleteFileHandler(interp, af->fp); + } + if (af->wEvent) { + Jim_DeleteFileHandler(interp, af->fp); + } + if (af->eEvent) { + Jim_DeleteFileHandler(interp, af->fp); + } +#endif + Jim_Free(af); +} + +static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + char buf[AIO_BUF_LEN]; + Jim_Obj *objPtr; + int nonewline = 0; + int neededLen = -1; /* -1 is "read as much as possible" */ + + if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) { + nonewline = 1; + argv++; + argc--; + } + if (argc == 1) { + jim_wide wideValue; + + if (Jim_GetWide(interp, argv[0], &wideValue) != JIM_OK) + return JIM_ERR; + if (wideValue < 0) { + Jim_SetResultString(interp, "invalid parameter: negative len", -1); + return JIM_ERR; + } + neededLen = (int)wideValue; + } + else if (argc) { + return -1; + } + objPtr = Jim_NewStringObj(interp, NULL, 0); + while (neededLen != 0) { + int retval; + int readlen; + + if (neededLen == -1) { + readlen = AIO_BUF_LEN; + } + else { + readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen); + } + retval = fread(buf, 1, readlen, af->fp); + if (retval > 0) { + Jim_AppendString(interp, objPtr, buf, retval); + if (neededLen != -1) { + neededLen -= retval; + } + } + if (retval != readlen) + break; + } + /* Check for error conditions */ + if (ferror(af->fp)) { + clearerr(af->fp); + /* eof and EAGAIN are not error conditions */ + if (!feof(af->fp) && errno != EAGAIN) { + /* I/O error */ + Jim_FreeNewObj(interp, objPtr); + JimAioSetError(interp, af->filename); + return JIM_ERR; + } + } + if (nonewline) { + int len; + const char *s = Jim_GetString(objPtr, &len); + + if (len > 0 && s[len - 1] == '\n') { + objPtr->length--; + objPtr->bytes[objPtr->length] = '\0'; + } + } + Jim_SetResult(interp, objPtr); + return JIM_OK; +} + +static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + long count = 0; + long maxlen = LONG_MAX; + FILE *outfh = Jim_AioFilehandle(interp, argv[0]); + + if (outfh == NULL) { + return JIM_ERR; + } + + if (argc == 2) { + if (Jim_GetLong(interp, argv[1], &maxlen) != JIM_OK) { + return JIM_ERR; + } + } + + while (count < maxlen) { + int ch = fgetc(af->fp); + + if (ch == EOF || fputc(ch, outfh) == EOF) { + break; + } + count++; + } + + if (ferror(af->fp)) { + Jim_SetResultFormatted(interp, "error while reading: %s", strerror(errno)); + clearerr(af->fp); + return JIM_ERR; + } + + if (ferror(outfh)) { + Jim_SetResultFormatted(interp, "error while writing: %s", strerror(errno)); + clearerr(outfh); + return JIM_ERR; + } + + Jim_SetResultInt(interp, count); + + return JIM_OK; +} + +static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + char buf[AIO_BUF_LEN]; + Jim_Obj *objPtr; + int len; + + errno = 0; + + objPtr = Jim_NewStringObj(interp, NULL, 0); + while (1) { + buf[AIO_BUF_LEN - 1] = '_'; + if (fgets(buf, AIO_BUF_LEN, af->fp) == NULL) + break; + + if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') { + Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1); + } + else { + len = strlen(buf); + + if (len && (buf[len - 1] == '\n')) { + /* strip "\n" */ + len--; + } + + Jim_AppendString(interp, objPtr, buf, len); + break; + } + } + if (ferror(af->fp) && errno != EAGAIN && errno != EINTR) { + /* I/O error */ + Jim_FreeNewObj(interp, objPtr); + JimAioSetError(interp, af->filename); + clearerr(af->fp); + return JIM_ERR; + } + + if (argc) { + if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) { + Jim_FreeNewObj(interp, objPtr); + return JIM_ERR; + } + + len = Jim_Length(objPtr); + + if (len == 0 && feof(af->fp)) { + /* On EOF returns -1 if varName was specified */ + len = -1; + } + Jim_SetResultInt(interp, len); + } + else { + Jim_SetResult(interp, objPtr); + } + return JIM_OK; +} + +static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + int wlen; + const char *wdata; + Jim_Obj *strObj; + + if (argc == 2) { + if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) { + return -1; + } + strObj = argv[1]; + } + else { + strObj = argv[0]; + } + + wdata = Jim_GetString(strObj, &wlen); + if (fwrite(wdata, 1, wlen, af->fp) == (unsigned)wlen) { + if (argc == 2 || putc('\n', af->fp) != EOF) { + return JIM_OK; + } + } + JimAioSetError(interp, af->filename); + return JIM_ERR; +} + +#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP) +static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + char *buf; + union sockaddr_any sa; + long len; + socklen_t salen = sizeof(sa); + int rlen; + + if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) { + return JIM_ERR; + } + + buf = Jim_Alloc(len + 1); + + rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen); + if (rlen < 0) { + Jim_Free(buf); + JimAioSetError(interp, NULL); + return JIM_ERR; + } + buf[rlen] = 0; + Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen)); + + if (argc > 1) { + /* INET6_ADDRSTRLEN is 46. Add some for [] and port */ + char addrbuf[60]; + +#if IPV6 + if (sa.sa.sa_family == PF_INET6) { + addrbuf[0] = '['; + /* Allow 9 for []:65535\0 */ + inet_ntop(sa.sa.sa_family, &sa.sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9); + snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa.sin.sin_port)); + } + else +#endif + { + /* Allow 7 for :65535\0 */ + inet_ntop(sa.sa.sa_family, &sa.sin.sin_addr, addrbuf, sizeof(addrbuf) - 7); + snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa.sin.sin_port)); + } + + if (Jim_SetVariable(interp, argv[1], Jim_NewStringObj(interp, addrbuf, -1)) != JIM_OK) { + return JIM_ERR; + } + } + + return JIM_OK; +} + + +static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + int wlen; + int len; + const char *wdata; + union sockaddr_any sa; + const char *addr = Jim_String(argv[1]); + int salen; + + if (IPV6 && af->addr_family == PF_INET6) { + if (JimParseIPv6Address(interp, addr, &sa, &salen) != JIM_OK) { + return JIM_ERR; + } + } + else if (JimParseIpAddress(interp, addr, &sa, &salen) != JIM_OK) { + return JIM_ERR; + } + wdata = Jim_GetString(argv[0], &wlen); + + /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */ + len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen); + if (len < 0) { + JimAioSetError(interp, NULL); + return JIM_ERR; + } + Jim_SetResultInt(interp, len); + return JIM_OK; +} + +static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + int sock; + union sockaddr_any sa; + socklen_t addrlen = sizeof(sa); + + sock = accept(af->fd, &sa.sa, &addrlen); + if (sock < 0) { + JimAioSetError(interp, NULL); + return JIM_ERR; + } + + /* Create the file command */ + return JimMakeChannel(interp, NULL, sock, Jim_NewStringObj(interp, "accept", -1), + "aio.sockstream%ld", af->addr_family, "r+"); +} + +static int aio_cmd_listen(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + long backlog; + + if (Jim_GetLong(interp, argv[0], &backlog) != JIM_OK) { + return JIM_ERR; + } + + if (listen(af->fd, backlog)) { + JimAioSetError(interp, NULL); + return JIM_ERR; + } + + return JIM_OK; +} +#endif /* JIM_BOOTSTRAP */ + +static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + + if (fflush(af->fp) == EOF) { + JimAioSetError(interp, af->filename); + return JIM_ERR; + } + return JIM_OK; +} + +static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + + Jim_SetResultInt(interp, feof(af->fp)); + return JIM_OK; +} + +static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_DeleteCommand(interp, Jim_String(argv[0])); + return JIM_OK; +} + +static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + int orig = SEEK_SET; + long offset; + + if (argc == 2) { + if (Jim_CompareStringImmediate(interp, argv[1], "start")) + orig = SEEK_SET; + else if (Jim_CompareStringImmediate(interp, argv[1], "current")) + orig = SEEK_CUR; + else if (Jim_CompareStringImmediate(interp, argv[1], "end")) + orig = SEEK_END; + else { + return -1; + } + } + if (Jim_GetLong(interp, argv[0], &offset) != JIM_OK) { + return JIM_ERR; + } + if (fseek(af->fp, offset, orig) == -1) { + JimAioSetError(interp, af->filename); + return JIM_ERR; + } + return JIM_OK; +} + +static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + + Jim_SetResultInt(interp, ftell(af->fp)); + return JIM_OK; +} + +static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + + Jim_SetResult(interp, af->filename); + return JIM_OK; +} + +#ifdef O_NDELAY +static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + + int fmode = af->flags; + + if (argc) { + long nb; + + if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) { + return JIM_ERR; + } + if (nb) { + fmode |= O_NDELAY; + } + else { + fmode &= ~O_NDELAY; + } + fcntl(af->fd, F_SETFL, fmode); + af->flags = fmode; + } + Jim_SetResultInt(interp, (fmode & O_NONBLOCK) ? 1 : 0); + return JIM_OK; +} +#endif + +static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + + static const char * const options[] = { + "none", + "line", + "full", + NULL + }; + enum + { + OPT_NONE, + OPT_LINE, + OPT_FULL, + }; + int option; + + if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; + } + switch (option) { + case OPT_NONE: + setvbuf(af->fp, NULL, _IONBF, 0); + break; + case OPT_LINE: + setvbuf(af->fp, NULL, _IOLBF, BUFSIZ); + break; + case OPT_FULL: + setvbuf(af->fp, NULL, _IOFBF, BUFSIZ); + break; + } + return JIM_OK; +} + +#ifdef jim_ext_eventloop +static void JimAioFileEventFinalizer(Jim_Interp *interp, void *clientData) +{ + Jim_Obj *objPtr = clientData; + + Jim_DecrRefCount(interp, objPtr); +} + +static int JimAioFileEventHandler(Jim_Interp *interp, void *clientData, int mask) +{ + Jim_Obj *objPtr = clientData; + + return Jim_EvalObjBackground(interp, objPtr); +} + +static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask, Jim_Obj **scriptHandlerObj, + int argc, Jim_Obj * const *argv) +{ + int scriptlen = 0; + + if (argc == 0) { + /* Return current script */ + if (*scriptHandlerObj) { + Jim_SetResult(interp, *scriptHandlerObj); + } + return JIM_OK; + } + + if (*scriptHandlerObj) { + /* Delete old handler */ + Jim_DeleteFileHandler(interp, af->fp); + *scriptHandlerObj = NULL; + } + + /* Now possibly add the new script(s) */ + Jim_GetString(argv[0], &scriptlen); + if (scriptlen == 0) { + /* Empty script, so done */ + return JIM_OK; + } + + /* A new script to add */ + Jim_IncrRefCount(argv[0]); + *scriptHandlerObj = argv[0]; + + Jim_CreateFileHandler(interp, af->fp, mask, + JimAioFileEventHandler, *scriptHandlerObj, JimAioFileEventFinalizer); + + return JIM_OK; +} + +static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + + return aio_eventinfo(interp, af, JIM_EVENT_READABLE, &af->rEvent, argc, argv); +} + +static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + + return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, &af->wEvent, argc, argv); +} + +static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + + return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->wEvent, argc, argv); +} +#endif + +static const jim_subcmd_type aio_command_table[] = { + { .cmd = "read", + .args = "?-nonewline? ?len?", + .function = aio_cmd_read, + .minargs = 0, + .maxargs = 2, + .description = "Read and return bytes from the stream. To eof if no len." + }, + { .cmd = "copyto", + .args = "handle ?size?", + .function = aio_cmd_copy, + .minargs = 1, + .maxargs = 2, + .description = "Copy up to 'size' bytes to the given filehandle, or to eof if no size." + }, + { .cmd = "gets", + .args = "?var?", + .function = aio_cmd_gets, + .minargs = 0, + .maxargs = 1, + .description = "Read one line and return it or store it in the var" + }, + { .cmd = "puts", + .args = "?-nonewline? str", + .function = aio_cmd_puts, + .minargs = 1, + .maxargs = 2, + .description = "Write the string, with newline unless -nonewline" + }, +#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP) + { .cmd = "recvfrom", + .args = "len ?addrvar?", + .function = aio_cmd_recvfrom, + .minargs = 1, + .maxargs = 2, + .description = "Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set" + }, + { .cmd = "sendto", + .args = "str address", + .function = aio_cmd_sendto, + .minargs = 2, + .maxargs = 2, + .description = "Send 'str' to the given address (dgram only)" + }, + { .cmd = "accept", + .function = aio_cmd_accept, + .description = "Server socket only: Accept a connection and return stream" + }, + { .cmd = "listen", + .args = "backlog", + .function = aio_cmd_listen, + .minargs = 1, + .maxargs = 1, + .description = "Set the listen backlog for server socket" + }, +#endif /* JIM_BOOTSTRAP */ + { .cmd = "flush", + .function = aio_cmd_flush, + .description = "Flush the stream" + }, + { .cmd = "eof", + .function = aio_cmd_eof, + .description = "Returns 1 if stream is at eof" + }, + { .cmd = "close", + .flags = JIM_MODFLAG_FULLARGV, + .function = aio_cmd_close, + .description = "Closes the stream" + }, + { .cmd = "seek", + .args = "offset ?start|current|end", + .function = aio_cmd_seek, + .minargs = 1, + .maxargs = 2, + .description = "Seeks in the stream (default 'current')" + }, + { .cmd = "tell", + .function = aio_cmd_tell, + .description = "Returns the current seek position" + }, + { .cmd = "filename", + .function = aio_cmd_filename, + .description = "Returns the original filename" + }, +#ifdef O_NDELAY + { .cmd = "ndelay", + .args = "?0|1?", + .function = aio_cmd_ndelay, + .minargs = 0, + .maxargs = 1, + .description = "Set O_NDELAY (if arg). Returns current/new setting." + }, +#endif + { .cmd = "buffering", + .args = "none|line|full", + .function = aio_cmd_buffering, + .minargs = 1, + .maxargs = 1, + .description = "Sets buffering" + }, +#ifdef jim_ext_eventloop + { .cmd = "readable", + .args = "?readable-script?", + .minargs = 0, + .maxargs = 1, + .function = aio_cmd_readable, + .description = "Returns script, or invoke readable-script when readable, {} to remove", + }, + { .cmd = "writable", + .args = "?writable-script?", + .minargs = 0, + .maxargs = 1, + .function = aio_cmd_writable, + .description = "Returns script, or invoke writable-script when writable, {} to remove", + }, + { .cmd = "onexception", + .args = "?exception-script?", + .minargs = 0, + .maxargs = 1, + .function = aio_cmd_onexception, + .description = "Returns script, or invoke exception-script when oob data, {} to remove", + }, +#endif + { 0 } +}; + +static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv); +} + +static int JimAioOpenCommand(Jim_Interp *interp, int argc, + Jim_Obj *const *argv) +{ + FILE *fp; + const char *hdlfmt; + const char *mode; + + if (argc != 2 && argc != 3) { + Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?"); + return JIM_ERR; + } + + mode = (argc == 3) ? Jim_String(argv[2]) : "r"; + hdlfmt = Jim_String(argv[1]); + if (Jim_CompareStringImmediate(interp, argv[1], "stdin")) { + fp = stdin; + } + else if (Jim_CompareStringImmediate(interp, argv[1], "stdout")) { + fp = stdout; + } + else if (Jim_CompareStringImmediate(interp, argv[1], "stderr")) { + fp = stderr; + } + else { + const char *filename = Jim_String(argv[1]); + + +#ifdef jim_ext_tclcompat + /* If the filename starts with '|', use popen instead */ + if (*filename == '|') { + Jim_Obj *evalObj[3]; + + evalObj[0] = Jim_NewStringObj(interp, "popen", -1); + evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1); + evalObj[2] = Jim_NewStringObj(interp, mode, -1); + + return Jim_EvalObjVector(interp, 3, evalObj); + } +#endif + hdlfmt = "aio.handle%ld"; + fp = NULL; + } + + /* Create the file command */ + return JimMakeChannel(interp, fp, -1, argv[1], hdlfmt, 0, mode); +} + +/** + * Creates a channel for fh/fd/filename. + * + * If fh is not NULL, uses that as the channel (and set AIO_KEEPOPEN). + * Otherwise, if fd is >= 0, uses that as the chanel. + * Otherwise opens 'filename' with mode 'mode'. + * + * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do. + * mode is used for open or fdopen. + * + * Creates the command and sets the name as the current result. + */ +static int JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename, + const char *hdlfmt, int family, const char *mode) +{ + AioFile *af; + char buf[AIO_CMD_LEN]; + int OpenFlags = 0; + + Jim_IncrRefCount(filename); + + if (fh == NULL) { + if (fd < 0) { + fh = fopen(Jim_String(filename), mode); + } + else { + fh = fdopen(fd, mode); + } + } + else { + OpenFlags = AIO_KEEPOPEN; + } + + if (fh == NULL) { + JimAioSetError(interp, filename); + close(fd); + Jim_DecrRefCount(interp, filename); + return JIM_ERR; + } + + /* Create the file command */ + af = Jim_Alloc(sizeof(*af)); + memset(af, 0, sizeof(*af)); + af->fp = fh; + af->fd = fileno(fh); + af->filename = filename; +#ifdef FD_CLOEXEC + if ((OpenFlags & AIO_KEEPOPEN) == 0) { + fcntl(af->fd, F_SETFD, FD_CLOEXEC); + af->OpenFlags = OpenFlags; + } +#endif +#ifdef O_NDELAY + af->flags = fcntl(af->fd, F_GETFL); +#endif + af->addr_family = family; + snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp)); + Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc); + + Jim_SetResultString(interp, buf, -1); + + return JIM_OK; +} + +#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP) + +static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *hdlfmt = "aio.unknown%ld"; + const char *socktypes[] = { + "unix", + "unix.server", + "dgram", + "dgram.server", + "stream", + "stream.server", + "pipe", + NULL + }; + enum + { + SOCK_UNIX, + SOCK_UNIX_SERVER, + SOCK_DGRAM_CLIENT, + SOCK_DGRAM_SERVER, + SOCK_STREAM_CLIENT, + SOCK_STREAM_SERVER, + SOCK_STREAM_PIPE, + SOCK_DGRAM6_CLIENT, + SOCK_DGRAM6_SERVER, + SOCK_STREAM6_CLIENT, + SOCK_STREAM6_SERVER, + }; + int socktype; + int sock; + const char *hostportarg = NULL; + int res; + int on = 1; + const char *mode = "r+"; + int family = PF_INET; + Jim_Obj *argv0 = argv[0]; + int ipv6 = 0; + + if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) { + if (!IPV6) { + Jim_SetResultString(interp, "ipv6 not supported", -1); + return JIM_ERR; + } + ipv6 = 1; + family = PF_INET6; + } + argc -= ipv6; + argv += ipv6; + + if (argc < 2) { + wrongargs: + Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?"); + return JIM_ERR; + } + + if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK) + return JIM_ERR; + + Jim_SetEmptyResult(interp); + + hdlfmt = "aio.sock%ld"; + + if (argc > 2) { + hostportarg = Jim_String(argv[2]); + } + + switch (socktype) { + case SOCK_DGRAM_CLIENT: + if (argc == 2) { + /* No address, so an unconnected dgram socket */ + sock = socket(family, SOCK_DGRAM, 0); + if (sock < 0) { + JimAioSetError(interp, NULL); + return JIM_ERR; + } + break; + } + /* fall through */ + case SOCK_STREAM_CLIENT: + { + union sockaddr_any sa; + int salen; + + if (argc != 3) { + goto wrongargs; + } + + if (ipv6) { + if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) { + return JIM_ERR; + } + } + else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) { + return JIM_ERR; + } + sock = socket(family, (socktype == SOCK_DGRAM_CLIENT) ? SOCK_DGRAM : SOCK_STREAM, 0); + if (sock < 0) { + JimAioSetError(interp, NULL); + return JIM_ERR; + } + res = connect(sock, &sa.sa, salen); + if (res) { + JimAioSetError(interp, argv[2]); + close(sock); + return JIM_ERR; + } + } + break; + + case SOCK_STREAM_SERVER: + case SOCK_DGRAM_SERVER: + { + union sockaddr_any sa; + int salen; + + if (argc != 3) { + goto wrongargs; + } + + if (ipv6) { + if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) { + return JIM_ERR; + } + } + else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) { + return JIM_ERR; + } + sock = socket(family, (socktype == SOCK_DGRAM_SERVER) ? SOCK_DGRAM : SOCK_STREAM, 0); + if (sock < 0) { + JimAioSetError(interp, NULL); + return JIM_ERR; + } + + /* Enable address reuse */ + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); + + res = bind(sock, &sa.sa, salen); + if (res) { + JimAioSetError(interp, argv[2]); + close(sock); + return JIM_ERR; + } + if (socktype == SOCK_STREAM_SERVER) { + res = listen(sock, 5); + if (res) { + JimAioSetError(interp, NULL); + close(sock); + return JIM_ERR; + } + } + hdlfmt = "aio.socksrv%ld"; + } + break; + +#ifdef HAVE_SYS_UN_H + case SOCK_UNIX: + { + struct sockaddr_un sa; + socklen_t len; + + if (argc != 3 || ipv6) { + goto wrongargs; + } + + if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) { + JimAioSetError(interp, argv[2]); + return JIM_ERR; + } + family = PF_UNIX; + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + JimAioSetError(interp, NULL); + return JIM_ERR; + } + len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family); + res = connect(sock, (struct sockaddr *)&sa, len); + if (res) { + JimAioSetError(interp, argv[2]); + close(sock); + return JIM_ERR; + } + hdlfmt = "aio.sockunix%ld"; + break; + } + + case SOCK_UNIX_SERVER: + { + struct sockaddr_un sa; + socklen_t len; + + if (argc != 3 || ipv6) { + goto wrongargs; + } + + if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) { + JimAioSetError(interp, argv[2]); + return JIM_ERR; + } + family = PF_UNIX; + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + JimAioSetError(interp, NULL); + return JIM_ERR; + } + len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family); + res = bind(sock, (struct sockaddr *)&sa, len); + if (res) { + JimAioSetError(interp, argv[2]); + close(sock); + return JIM_ERR; + } + res = listen(sock, 5); + if (res) { + JimAioSetError(interp, NULL); + close(sock); + return JIM_ERR; + } + hdlfmt = "aio.sockunixsrv%ld"; + break; + } +#endif + +#ifdef HAVE_PIPE + case SOCK_STREAM_PIPE: + { + int p[2]; + + if (argc != 2 || ipv6) { + goto wrongargs; + } + + if (pipe(p) < 0) { + JimAioSetError(interp, NULL); + return JIM_ERR; + } + + if (JimMakeChannel(interp, NULL, p[0], argv[1], "aio.pipe%ld", 0, "r") == JIM_OK) { + Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0); + Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp)); + + if (JimMakeChannel(interp, NULL, p[1], argv[1], "aio.pipe%ld", 0, "w") == JIM_OK) { + Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp)); + Jim_SetResult(interp, objPtr); + return JIM_OK; + } + } + /* Can only be here if fdopen() failed */ + close(p[0]); + close(p[1]); + JimAioSetError(interp, NULL); + return JIM_ERR; + } + break; +#endif + default: + Jim_SetResultString(interp, "Unsupported socket type", -1); + return JIM_ERR; + } + + return JimMakeChannel(interp, NULL, sock, argv[1], hdlfmt, family, mode); +} +#endif /* JIM_BOOTSTRAP */ + +FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command) +{ + Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG); + + if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) { + return ((AioFile *) cmdPtr->u.native.privData)->fp; + } + Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command); + return NULL; +} + +int Jim_aioInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG)) + return JIM_ERR; + + Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL); +#ifndef JIM_ANSIC + Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL); +#endif + + /* Takeover stdin, stdout and stderr */ + Jim_EvalGlobal(interp, "open stdin; open stdout; open stderr"); + + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-array.c b/release/src/router/usbmodeswitch/jim/jim-array.c new file mode 100644 index 0000000000..89a86f0710 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-array.c @@ -0,0 +1,274 @@ + +/* + * Implements the array command for jim + * + * (c) 2008 Steve Bennett + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + * + * Based on code originally from Tcl 6.7: + * + * Copyright 1987-1991 Regents of the University of California + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +#include +#include +#include +#include +#include +#include + +#include "jim.h" +#include "jimautoconf.h" +#include "jim-subcmd.h" + +static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + /* Just a regular [info exists] */ + Jim_SetResultInt(interp, Jim_GetVariable(interp, argv[0], 0) != 0); + return JIM_OK; +} + +static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int i; + int len; + int all = 0; + Jim_Obj *resultObj; + Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); + Jim_Obj *dictObj; + Jim_Obj **dictValuesObj; + + if (!objPtr) { + return JIM_OK; + } + + if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) { + all = 1; + } + + /* If it is a dictionary or list with an even number of elements, nothing else to do */ + if (all) { + if (Jim_IsDict(objPtr) || (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0)) { + Jim_SetResult(interp, objPtr); + return JIM_OK; + } + } + + if (Jim_DictKeysVector(interp, objPtr, NULL, 0, &dictObj, JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; + } + + if (Jim_DictPairs(interp, dictObj, &dictValuesObj, &len) != JIM_OK) { + return JIM_ERR; + } + + if (all) { + /* Return the whole array */ + Jim_SetResult(interp, dictObj); + } + else { + /* Only return the matching values */ + resultObj = Jim_NewListObj(interp, NULL, 0); + + for (i = 0; i < len; i += 2) { + if (Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) { + Jim_ListAppendElement(interp, resultObj, dictValuesObj[i]); + Jim_ListAppendElement(interp, resultObj, dictValuesObj[i + 1]); + } + } + + Jim_SetResult(interp, resultObj); + } + Jim_Free(dictValuesObj); + return JIM_OK; + +} + +static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); + + if (!objPtr) { + return JIM_OK; + } + + return Jim_DictKeys(interp, objPtr, argc == 1 ? NULL : argv[1]); +} + +static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int i; + int len; + Jim_Obj *resultObj; + Jim_Obj *objPtr; + Jim_Obj *dictObj; + Jim_Obj **dictValuesObj; + + if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) { + /* Unset the whole array */ + Jim_UnsetVariable(interp, argv[0], JIM_NONE); + return JIM_OK; + } + + objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); + + if (Jim_DictKeysVector(interp, objPtr, NULL, 0, &dictObj, JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; + } + + if (Jim_DictPairs(interp, dictObj, &dictValuesObj, &len) != JIM_OK) { + return JIM_ERR; + } + + /* Create a new object with the values which don't match */ + resultObj = Jim_NewDictObj(interp, NULL, 0); + + for (i = 0; i < len; i += 2) { + if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) { + Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]); + } + } + Jim_Free(dictValuesObj); + + Jim_SetVariable(interp, argv[0], resultObj); + return JIM_OK; +} + +static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *objPtr; + int len = 0; + + /* Not found means zero length */ + objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); + if (objPtr) { + len = Jim_DictSize(interp, objPtr); + if (len < 0) { + return JIM_ERR; + } + } + + Jim_SetResultInt(interp, len); + + return JIM_OK; +} + +static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int i; + int len; + int rc = JIM_OK; + Jim_Obj *listObj = argv[1]; + + if (Jim_GetVariable(interp, argv[0], JIM_NONE) == NULL) { + /* Doesn't exist, so just set the list directly */ + return Jim_SetVariable(interp, argv[0], listObj); + } + + len = Jim_ListLength(interp, listObj); + if (len % 2) { + Jim_SetResultString(interp, "list must have an even number of elements", -1); + return JIM_ERR; + } + for (i = 0; i < len && rc == JIM_OK; i += 2) { + Jim_Obj *nameObj; + Jim_Obj *valueObj; + + Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE); + Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE); + + rc = Jim_SetDictKeysVector(interp, argv[0], &nameObj, 1, valueObj, JIM_ERRMSG); + } + + return rc; +} + +static const jim_subcmd_type array_command_table[] = { + { .cmd = "exists", + .args = "arrayName", + .function = array_cmd_exists, + .minargs = 1, + .maxargs = 1, + .description = "Does array exist?" + }, + { .cmd = "get", + .args = "arrayName ?pattern?", + .function = array_cmd_get, + .minargs = 1, + .maxargs = 2, + .description = "Array contents as name value list" + }, + { .cmd = "names", + .args = "arrayName ?pattern?", + .function = array_cmd_names, + .minargs = 1, + .maxargs = 2, + .description = "Array keys as a list" + }, + { .cmd = "set", + .args = "arrayName list", + .function = array_cmd_set, + .minargs = 2, + .maxargs = 2, + .description = "Set array from list" + }, + { .cmd = "size", + .args = "arrayName", + .function = array_cmd_size, + .minargs = 1, + .maxargs = 1, + .description = "Number of elements in array" + }, + { .cmd = "unset", + .args = "arrayName ?pattern?", + .function = array_cmd_unset, + .minargs = 1, + .maxargs = 2, + .description = "Unset elements of an array" + }, + { .cmd = 0, + } +}; + +int Jim_arrayInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "array", "1.0", JIM_ERRMSG)) + return JIM_ERR; + + Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL); + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-clock.c b/release/src/router/usbmodeswitch/jim/jim-clock.c new file mode 100644 index 0000000000..51ffb511bd --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-clock.c @@ -0,0 +1,159 @@ + +/* + * tcl_clock.c + * + * Implements the clock command + */ + +/* For strptime() */ +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 500 +#endif + +#include +#include +#include +#include +#include + +#include "jim.h" +#include "jimautoconf.h" +#include "jim-subcmd.h" + +static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + /* How big is big enough? */ + char buf[100]; + time_t t; + long seconds; + + const char *format = "%a %b %d %H:%M:%S %Z %Y"; + + if (argc == 2 || (argc == 3 && !Jim_CompareStringImmediate(interp, argv[1], "-format"))) { + return -1; + } + + if (argc == 3) { + format = Jim_String(argv[2]); + } + + if (Jim_GetLong(interp, argv[0], &seconds) != JIM_OK) { + return JIM_ERR; + } + t = seconds; + + strftime(buf, sizeof(buf), format, localtime(&t)); + + Jim_SetResultString(interp, buf, -1); + + return JIM_OK; +} + +#ifdef HAVE_STRPTIME +static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + char *pt; + struct tm tm; + time_t now = time(0); + + if (!Jim_CompareStringImmediate(interp, argv[1], "-format")) { + return -1; + } + + /* Initialise with the current date/time */ + localtime_r(&now, &tm); + + pt = strptime(Jim_String(argv[0]), Jim_String(argv[2]), &tm); + if (pt == 0 || *pt != 0) { + Jim_SetResultString(interp, "Failed to parse time according to format", -1); + return JIM_ERR; + } + + /* Now convert into a time_t */ + Jim_SetResultInt(interp, mktime(&tm)); + + return JIM_OK; +} +#endif + +static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_SetResultInt(interp, time(NULL)); + + return JIM_OK; +} + +static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec); + + return JIM_OK; +} + +static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000 + tv.tv_usec / 1000); + + return JIM_OK; +} + +static const jim_subcmd_type clock_command_table[] = { + { .cmd = "seconds", + .function = clock_cmd_seconds, + .minargs = 0, + .maxargs = 0, + .description = "Returns the current time as seconds since the epoch" + }, + { .cmd = "clicks", + .function = clock_cmd_micros, + .minargs = 0, + .maxargs = 0, + .description = "Returns the current time in 'clicks'" + }, + { .cmd = "microseconds", + .function = clock_cmd_micros, + .minargs = 0, + .maxargs = 0, + .description = "Returns the current time in microseconds" + }, + { .cmd = "milliseconds", + .function = clock_cmd_millis, + .minargs = 0, + .maxargs = 0, + .description = "Returns the current time in milliseconds" + }, + { .cmd = "format", + .args = "seconds ?-format format?", + .function = clock_cmd_format, + .minargs = 1, + .maxargs = 3, + .description = "Format the given time" + }, +#ifdef HAVE_STRPTIME + { .cmd = "scan", + .args = "str -format format", + .function = clock_cmd_scan, + .minargs = 3, + .maxargs = 3, + .description = "Determine the time according to the given format" + }, +#endif + { 0 } +}; + +int Jim_clockInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "clock", "1.0", JIM_ERRMSG)) + return JIM_ERR; + + Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL); + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-config.h.in b/release/src/router/usbmodeswitch/jim/jim-config.h.in new file mode 100644 index 0000000000..2394bc59cb --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-config.h.in @@ -0,0 +1,2 @@ +/* Public autoconf settings */ +@DEFINE_HAVE_LONG_LONG@ diff --git a/release/src/router/usbmodeswitch/jim/jim-eventloop.c b/release/src/router/usbmodeswitch/jim/jim-eventloop.c new file mode 100644 index 0000000000..a7efdc28a1 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-eventloop.c @@ -0,0 +1,760 @@ + +/* Jim - A small embeddable Tcl interpreter + * + * Copyright 2005 Salvatore Sanfilippo + * Copyright 2005 Clemens Hintze + * Copyright 2005 patthoyts - Pat Thoyts + * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com + * Copyright 2008 Andrew Lunn + * Copyright 2008 Duane Ellis + * Copyright 2008 Uwe Klein + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + **/ + +#include "jim.h" +#include "jimautoconf.h" +#include "jim-eventloop.h" + +/* POSIX includes */ +#include +#include +#include +#include +#include + +#if defined(__MINGW32__) +#include +#include +#define msleep Sleep +#ifndef HAVE_USLEEP +#define usleep(US) msleep((US) / 1000) +#endif +#else +#include + +#ifndef HAVE_USLEEP +/* XXX: Implement this in terms of select() or nanosleep() */ +#define usleep(US) +#endif +#define msleep(MS) sleep((MS) / 1000); usleep(((MS) % 1000) * 1000); +#endif + +/* --- */ + +/* File event structure */ +typedef struct Jim_FileEvent +{ + FILE *handle; + int mask; /* one of JIM_EVENT_(READABLE|WRITABLE|EXCEPTION) */ + Jim_FileProc *fileProc; + Jim_EventFinalizerProc *finalizerProc; + void *clientData; + struct Jim_FileEvent *next; +} Jim_FileEvent; + +/* Time event structure */ +typedef struct Jim_TimeEvent +{ + jim_wide id; /* time event identifier. */ + int mode; /* restart, repetitive .. UK */ + long initialms; /* initial relativ timer value UK */ + long when_sec; /* seconds */ + long when_ms; /* milliseconds */ + Jim_TimeProc *timeProc; + Jim_EventFinalizerProc *finalizerProc; + void *clientData; + struct Jim_TimeEvent *next; +} Jim_TimeEvent; + +/* Per-interp stucture containing the state of the event loop */ +typedef struct Jim_EventLoop +{ + jim_wide timeEventNextId; + Jim_FileEvent *fileEventHead; + Jim_TimeEvent *timeEventHead; + int suppress_bgerror; /* bgerror returned break, so don't call it again */ +} Jim_EventLoop; + +static void JimAfterTimeHandler(Jim_Interp *interp, void *clientData); +static void JimAfterTimeEventFinalizer(Jim_Interp *interp, void *clientData); + +int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr) +{ + Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); + Jim_CallFrame *savedFramePtr; + int retval; + + savedFramePtr = interp->framePtr; + interp->framePtr = interp->topFramePtr; + retval = Jim_EvalObj(interp, scriptObjPtr); + interp->framePtr = savedFramePtr; + /* Try to report the error (if any) via the bgerror proc */ + if (retval != JIM_OK && !eventLoop->suppress_bgerror) { + Jim_Obj *objv[2]; + int rc = JIM_ERR; + + objv[0] = Jim_NewStringObj(interp, "bgerror", -1); + objv[1] = Jim_GetResult(interp); + Jim_IncrRefCount(objv[0]); + Jim_IncrRefCount(objv[1]); + if (Jim_GetCommand(interp, objv[0], JIM_NONE) == NULL || (rc = Jim_EvalObjVector(interp, 2, objv)) != JIM_OK) { + if (rc == JIM_BREAK) { + /* No more bgerror calls */ + eventLoop->suppress_bgerror++; + } + else { + /* Report the error to stderr. */ + Jim_MakeErrorMessage(interp); + fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp))); + /* And reset the result */ + Jim_SetResultString(interp, "", -1); + } + } + Jim_DecrRefCount(interp, objv[0]); + Jim_DecrRefCount(interp, objv[1]); + } + return retval; +} + + +void Jim_CreateFileHandler(Jim_Interp *interp, FILE * handle, int mask, + Jim_FileProc * proc, void *clientData, Jim_EventFinalizerProc * finalizerProc) +{ + Jim_FileEvent *fe; + Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); + + fe = Jim_Alloc(sizeof(*fe)); + fe->handle = handle; + fe->mask = mask; + fe->fileProc = proc; + fe->finalizerProc = finalizerProc; + fe->clientData = clientData; + fe->next = eventLoop->fileEventHead; + eventLoop->fileEventHead = fe; +} + +void Jim_DeleteFileHandler(Jim_Interp *interp, FILE * handle) +{ + Jim_FileEvent *fe, *prev = NULL; + Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); + + fe = eventLoop->fileEventHead; + while (fe) { + if (fe->handle == handle) { + if (prev == NULL) + eventLoop->fileEventHead = fe->next; + else + prev->next = fe->next; + if (fe->finalizerProc) + fe->finalizerProc(interp, fe->clientData); + Jim_Free(fe); + return; + } + prev = fe; + fe = fe->next; + } +} + +static void JimGetTime(long *seconds, long *milliseconds) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + *seconds = tv.tv_sec; + *milliseconds = tv.tv_usec / 1000; +} + +jim_wide Jim_CreateTimeHandler(Jim_Interp *interp, jim_wide milliseconds, + Jim_TimeProc * proc, void *clientData, Jim_EventFinalizerProc * finalizerProc) +{ + Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); + jim_wide id = eventLoop->timeEventNextId++; + Jim_TimeEvent *te, *e, *prev; + long cur_sec, cur_ms; + + JimGetTime(&cur_sec, &cur_ms); + + te = Jim_Alloc(sizeof(*te)); + te->id = id; + te->mode = 0; + te->initialms = milliseconds; + te->when_sec = cur_sec + milliseconds / 1000; + te->when_ms = cur_ms + milliseconds % 1000; + if (te->when_ms >= 1000) { + te->when_sec++; + te->when_ms -= 1000; + } + te->timeProc = proc; + te->finalizerProc = finalizerProc; + te->clientData = clientData; + + /* Add to the appropriate place in the list */ + if (eventLoop->timeEventHead) { + prev = NULL; + for (e = eventLoop->timeEventHead; e; e = e->next) { + if (te->when_sec < e->when_sec || (te->when_sec == e->when_sec && te->when_ms < e->when_ms)) { + break; + } + prev = e; + } + if (prev) { + te->next = prev->next; + prev->next = te; + return id; + } + } + + te->next = eventLoop->timeEventHead; + eventLoop->timeEventHead = te; + + return id; +} + +static jim_wide JimParseAfterId(Jim_Obj *idObj) +{ + int len; + const char *tok = Jim_GetString(idObj, &len); + jim_wide id; + + if (strncmp(tok, "after#", 6) == 0 && Jim_StringToWide(tok + 6, &id, 10) == JIM_OK) { + /* Got an event by id */ + return id; + } + return -1; +} + +static jim_wide JimFindAfterByScript(Jim_EventLoop *eventLoop, Jim_Obj *scriptObj) +{ + Jim_TimeEvent *te; + + for (te = eventLoop->timeEventHead; te; te = te->next) { + /* Is this an 'after' event? */ + if (te->timeProc == JimAfterTimeHandler) { + if (Jim_StringEqObj(scriptObj, te->clientData)) { + return te->id; + } + } + } + return -1; /* NO event with the specified ID found */ +} + +static Jim_TimeEvent *JimFindTimeHandlerById(Jim_EventLoop *eventLoop, jim_wide id) +{ + Jim_TimeEvent *te; + + for (te = eventLoop->timeEventHead; te; te = te->next) { + if (te->id == id) { + return te; + } + } + return NULL; +} + +static Jim_TimeEvent *Jim_RemoveTimeHandler(Jim_EventLoop *eventLoop, jim_wide id) +{ + Jim_TimeEvent *te, *prev = NULL; + + for (te = eventLoop->timeEventHead; te; te = te->next) { + if (te->id == id) { + if (prev == NULL) + eventLoop->timeEventHead = te->next; + else + prev->next = te->next; + return te; + } + prev = te; + } + return NULL; +} + +static void Jim_FreeTimeHandler(Jim_Interp *interp, Jim_TimeEvent *te) +{ + if (te->finalizerProc) + te->finalizerProc(interp, te->clientData); + Jim_Free(te); +} + +jim_wide Jim_DeleteTimeHandler(Jim_Interp *interp, jim_wide id) +{ + Jim_TimeEvent *te; + Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); + + if (id >= eventLoop->timeEventNextId) { + return -2; /* wrong event ID */ + } + + te = Jim_RemoveTimeHandler(eventLoop, id); + if (te) { + jim_wide remain; + long cur_sec, cur_ms; + + JimGetTime(&cur_sec, &cur_ms); + + remain = (te->when_sec - cur_sec) * 1000; + remain += (te->when_ms - cur_ms); + remain = (remain < 0) ? 0 : remain; + + Jim_FreeTimeHandler(interp, te); + return remain; + } + return -1; /* NO event with the specified ID found */ +} + +/* --- POSIX version of Jim_ProcessEvents, for now the only available --- */ + +/* Process every pending time event, then every pending file event + * (that may be registered by time event callbacks just processed). + * Without special flags the function sleeps until some file event + * fires, or when the next time event occurrs (if any). + * + * If flags is 0, the function does nothing and returns. + * if flags has JIM_ALL_EVENTS set, all the kind of events are processed. + * if flags has JIM_FILE_EVENTS set, file events are processed. + * if flags has JIM_TIME_EVENTS set, time events are processed. + * if flags has JIM_DONT_WAIT set the function returns ASAP until all + * the events that's possible to process without to wait are processed. + * + * The function returns the number of events processed or -1 if + * there are no matching handlers, or -2 on error. + */ +int Jim_ProcessEvents(Jim_Interp *interp, int flags) +{ + jim_wide sleep_ms = -1; + int processed = 0; + Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); + Jim_FileEvent *fe = eventLoop->fileEventHead; + Jim_TimeEvent *te; + jim_wide maxId; + + if ((flags & JIM_FILE_EVENTS) == 0 || fe == NULL) { + /* No file events */ + if ((flags & JIM_TIME_EVENTS) == 0 || eventLoop->timeEventHead == NULL) { + /* No time events */ + return -1; + } + } + + /* Note that we want call select() even if there are no + * file events to process as long as we want to process time + * events, in order to sleep until the next time event is ready + * to fire. */ + + if (flags & JIM_DONT_WAIT) { + /* Wait no time */ + sleep_ms = 0; + } + else if (flags & JIM_TIME_EVENTS) { + /* The nearest timer is always at the head of the list */ + if (eventLoop->timeEventHead) { + Jim_TimeEvent *shortest = eventLoop->timeEventHead; + long now_sec, now_ms; + + /* Calculate the time missing for the nearest + * timer to fire. */ + JimGetTime(&now_sec, &now_ms); + sleep_ms = 1000 * (shortest->when_sec - now_sec) + (shortest->when_ms - now_ms); + if (sleep_ms < 0) { + sleep_ms = 1; + } + } + else { + /* Wait forever */ + sleep_ms = -1; + } + } + +#ifdef HAVE_SELECT + if (flags & JIM_FILE_EVENTS) { + int retval; + struct timeval tv, *tvp = NULL; + fd_set rfds, wfds, efds; + int maxfd = -1; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + + /* Check file events */ + while (fe != NULL) { + int fd = fileno(fe->handle); + + if (fe->mask & JIM_EVENT_READABLE) + FD_SET(fd, &rfds); + if (fe->mask & JIM_EVENT_WRITABLE) + FD_SET(fd, &wfds); + if (fe->mask & JIM_EVENT_EXCEPTION) + FD_SET(fd, &efds); + if (maxfd < fd) + maxfd = fd; + fe = fe->next; + } + + if (sleep_ms >= 0) { + tvp = &tv; + tvp->tv_sec = sleep_ms / 1000; + tvp->tv_usec = 1000 * (sleep_ms % 1000); + } + + retval = select(maxfd + 1, &rfds, &wfds, &efds, tvp); + + if (retval < 0) { + if (errno == EINVAL) { + /* This can happen on mingw32 if a non-socket filehandle is passed */ + Jim_SetResultString(interp, "non-waitable filehandle", -1); + return -2; + } + /* XXX: What about EINTR? */ + } + else if (retval > 0) { + fe = eventLoop->fileEventHead; + while (fe != NULL) { + int fd = fileno(fe->handle); + int mask = 0; + + if ((fe->mask & JIM_EVENT_READABLE) && FD_ISSET(fd, &rfds)) + mask |= JIM_EVENT_READABLE; + if (fe->mask & JIM_EVENT_WRITABLE && FD_ISSET(fd, &wfds)) + mask |= JIM_EVENT_WRITABLE; + if (fe->mask & JIM_EVENT_EXCEPTION && FD_ISSET(fd, &efds)) + mask |= JIM_EVENT_EXCEPTION; + + if (mask) { + if (fe->fileProc(interp, fe->clientData, mask) != JIM_OK) { + /* Remove the element on handler error */ + Jim_DeleteFileHandler(interp, fe->handle); + } + processed++; + /* After an event is processed our file event list + * may no longer be the same, so what we do + * is to clear the bit for this file descriptor and + * restart again from the head. */ + fe = eventLoop->fileEventHead; + FD_CLR(fd, &rfds); + FD_CLR(fd, &wfds); + FD_CLR(fd, &efds); + } + else { + fe = fe->next; + } + } + } + } +#else + if (sleep_ms > 0) { + msleep(sleep_ms); + } +#endif + + /* Check time events */ + te = eventLoop->timeEventHead; + maxId = eventLoop->timeEventNextId - 1; + while (te) { + long now_sec, now_ms; + jim_wide id; + + if (te->id > maxId) { + te = te->next; + continue; + } + JimGetTime(&now_sec, &now_ms); + if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) { + id = te->id; + /* Remove from the list before executing */ + Jim_RemoveTimeHandler(eventLoop, id); + te->timeProc(interp, te->clientData); + /* After an event is processed our time event list may + * no longer be the same, so we restart from head. + * Still we make sure to don't process events registered + * by event handlers itself in order to don't loop forever + * even in case an [after 0] that continuously register + * itself. To do so we saved the max ID we want to handle. */ + Jim_FreeTimeHandler(interp, te); + + te = eventLoop->timeEventHead; + processed++; + } + else { + te = te->next; + } + } + + return processed; +} + +/* ---------------------------------------------------------------------- */ + +static void JimELAssocDataDeleProc(Jim_Interp *interp, void *data) +{ + void *next; + Jim_FileEvent *fe; + Jim_TimeEvent *te; + Jim_EventLoop *eventLoop = data; + + fe = eventLoop->fileEventHead; + while (fe) { + next = fe->next; + if (fe->finalizerProc) + fe->finalizerProc(interp, fe->clientData); + Jim_Free(fe); + fe = next; + } + + te = eventLoop->timeEventHead; + while (te) { + next = te->next; + if (te->finalizerProc) + te->finalizerProc(interp, te->clientData); + Jim_Free(te); + te = next; + } + Jim_Free(data); +} + +static int JimELVwaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_EventLoop *eventLoop = Jim_CmdPrivData(interp); + Jim_Obj *oldValue; + int rc; + + if (argc != 2) { + Jim_WrongNumArgs(interp, 1, argv, "name"); + return JIM_ERR; + } + + oldValue = Jim_GetGlobalVariable(interp, argv[1], JIM_NONE); + if (oldValue) { + Jim_IncrRefCount(oldValue); + } + else { + /* If a result was left, it is an error */ + int len; + Jim_GetString(interp->result, &len); + if (len) { + return JIM_ERR; + } + } + + eventLoop->suppress_bgerror = 0; + + while ((rc = Jim_ProcessEvents(interp, JIM_ALL_EVENTS)) >= 0) { + Jim_Obj *currValue; + currValue = Jim_GetGlobalVariable(interp, argv[1], JIM_NONE); + /* Stop the loop if the vwait-ed variable changed value, + * or if was unset and now is set (or the contrary). */ + if ((oldValue && !currValue) || + (!oldValue && currValue) || + (oldValue && currValue && !Jim_StringEqObj(oldValue, currValue))) + break; + } + if (oldValue) + Jim_DecrRefCount(interp, oldValue); + + + if (rc == -2) { + return JIM_ERR; + } + + Jim_SetEmptyResult(interp); + return JIM_OK; +} + +static int JimELUpdateCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_EventLoop *eventLoop = Jim_CmdPrivData(interp); + static const char * const options[] = { + "idletasks", NULL + }; + enum { UPDATE_IDLE, UPDATE_NONE }; + int option = UPDATE_NONE; + int flags = JIM_TIME_EVENTS; + + if (argc == 1) { + flags = JIM_ALL_EVENTS; + } + else if (argc > 2 || Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { + Jim_WrongNumArgs(interp, 1, argv, "?idletasks?"); + return JIM_ERR; + } + + eventLoop->suppress_bgerror = 0; + + while (Jim_ProcessEvents(interp, flags | JIM_DONT_WAIT) > 0) { + } + + return JIM_OK; +} + +static void JimAfterTimeHandler(Jim_Interp *interp, void *clientData) +{ + Jim_Obj *objPtr = clientData; + + Jim_EvalObjBackground(interp, objPtr); +} + +static void JimAfterTimeEventFinalizer(Jim_Interp *interp, void *clientData) +{ + Jim_Obj *objPtr = clientData; + + Jim_DecrRefCount(interp, objPtr); +} + +static int JimELAfterCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_EventLoop *eventLoop = Jim_CmdPrivData(interp); + jim_wide ms = 0, id; + Jim_Obj *objPtr, *idObjPtr; + static const char * const options[] = { + "cancel", "info", "idle", NULL + }; + enum + { AFTER_CANCEL, AFTER_INFO, AFTER_IDLE, AFTER_RESTART, AFTER_EXPIRE, AFTER_CREATE }; + int option = AFTER_CREATE; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "option ?arg ...?"); + return JIM_ERR; + } + if (Jim_GetWide(interp, argv[1], &ms) != JIM_OK) { + if (Jim_GetEnum(interp, argv[1], options, &option, "argument", JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; + } + Jim_SetEmptyResult(interp); + } + else if (argc == 2) { + /* Simply a sleep */ + msleep(ms); + return JIM_OK; + } + + switch (option) { + case AFTER_IDLE: + if (argc < 3) { + Jim_WrongNumArgs(interp, 2, argv, "script ?script ...?"); + return JIM_ERR; + } + /* fall through */ + case AFTER_CREATE: { + Jim_Obj *scriptObj = Jim_ConcatObj(interp, argc - 2, argv + 2); + Jim_IncrRefCount(scriptObj); + id = Jim_CreateTimeHandler(interp, ms, JimAfterTimeHandler, scriptObj, + JimAfterTimeEventFinalizer); + objPtr = Jim_NewStringObj(interp, NULL, 0); + Jim_AppendString(interp, objPtr, "after#", -1); + idObjPtr = Jim_NewIntObj(interp, id); + Jim_IncrRefCount(idObjPtr); + Jim_AppendObj(interp, objPtr, idObjPtr); + Jim_DecrRefCount(interp, idObjPtr); + Jim_SetResult(interp, objPtr); + return JIM_OK; + } + case AFTER_CANCEL: + if (argc < 3) { + Jim_WrongNumArgs(interp, 2, argv, "id|command"); + return JIM_ERR; + } + else { + jim_wide remain = 0; + + id = JimParseAfterId(argv[2]); + if (id < 0) { + /* Not an event id, so search by script */ + Jim_Obj *scriptObj = Jim_ConcatObj(interp, argc - 2, argv + 2); + id = JimFindAfterByScript(eventLoop, scriptObj); + Jim_FreeNewObj(interp, scriptObj); + if (id < 0) { + /* Not found */ + break; + } + } + remain = Jim_DeleteTimeHandler(interp, id); + if (remain >= 0) { + Jim_SetResultInt(interp, remain); + } + } + break; + + case AFTER_INFO: + if (argc == 2) { + Jim_TimeEvent *te = eventLoop->timeEventHead; + Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); + char buf[30]; + const char *fmt = "after#%" JIM_WIDE_MODIFIER; + + while (te) { + snprintf(buf, sizeof(buf), fmt, te->id); + Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, buf, -1)); + te = te->next; + } + Jim_SetResult(interp, listObj); + } + else if (argc == 3) { + id = JimParseAfterId(argv[2]); + if (id >= 0) { + Jim_TimeEvent *e = JimFindTimeHandlerById(eventLoop, id); + if (e && e->timeProc == JimAfterTimeHandler) { + Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); + Jim_ListAppendElement(interp, listObj, e->clientData); + Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, e->initialms ? "timer" : "idle", -1)); + Jim_SetResult(interp, listObj); + return JIM_OK; + } + } + Jim_SetResultFormatted(interp, "event \"%#s\" doesn't exist", argv[2]); + return JIM_ERR; + } + else { + Jim_WrongNumArgs(interp, 2, argv, "?id?"); + return JIM_ERR; + } + break; + } + return JIM_OK; +} + +int Jim_eventloopInit(Jim_Interp *interp) +{ + Jim_EventLoop *eventLoop; + + if (Jim_PackageProvide(interp, "eventloop", "1.0", JIM_ERRMSG)) + return JIM_ERR; + + eventLoop = Jim_Alloc(sizeof(*eventLoop)); + eventLoop->fileEventHead = NULL; + eventLoop->timeEventHead = NULL; + eventLoop->timeEventNextId = 1; + eventLoop->suppress_bgerror = 0; + Jim_SetAssocData(interp, "eventloop", JimELAssocDataDeleProc, eventLoop); + + Jim_CreateCommand(interp, "vwait", JimELVwaitCommand, eventLoop, NULL); + Jim_CreateCommand(interp, "update", JimELUpdateCommand, eventLoop, NULL); + Jim_CreateCommand(interp, "after", JimELAfterCommand, eventLoop, NULL); + + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-eventloop.h b/release/src/router/usbmodeswitch/jim/jim-eventloop.h new file mode 100644 index 0000000000..4da5408f0f --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-eventloop.h @@ -0,0 +1,87 @@ +/* Jim - A small embeddable Tcl interpreter + * + * Copyright 2005 Salvatore Sanfilippo + * Copyright 2005 Clemens Hintze + * Copyright 2005 patthoyts - Pat Thoyts + * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com + * Copyright 2008 Andrew Lunn + * Copyright 2008 Duane Ellis + * Copyright 2008 Uwe Klein + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + **/ +/* ------ USAGE ------- + * + * In order to use this file from other extensions include it in every + * file where you need to call the eventloop API, also in the init + * function of your extension call Jim_ImportEventloopAPI(interp) + * after the Jim_InitExtension() call. + * + * See the UDP extension as example. + */ + + +#ifndef __JIM_EVENTLOOP_H__ +#define __JIM_EVENTLOOP_H__ + +#include + +typedef int Jim_FileProc(Jim_Interp *interp, void *clientData, int mask); +typedef int Jim_SignalProc(Jim_Interp *interp, void *clientData, void *msg); +typedef void Jim_TimeProc(Jim_Interp *interp, void *clientData); +typedef void Jim_EventFinalizerProc(Jim_Interp *interp, void *clientData); + +/* File event structure */ +#define JIM_EVENT_READABLE 1 +#define JIM_EVENT_WRITABLE 2 +#define JIM_EVENT_EXCEPTION 4 + +JIM_EXPORT void Jim_CreateFileHandler (Jim_Interp *interp, + FILE *handle, int mask, + Jim_FileProc *proc, void *clientData, + Jim_EventFinalizerProc *finalizerProc); +JIM_EXPORT void Jim_DeleteFileHandler (Jim_Interp *interp, + FILE *handle); +JIM_EXPORT jim_wide Jim_CreateTimeHandler (Jim_Interp *interp, + jim_wide milliseconds, + Jim_TimeProc *proc, void *clientData, + Jim_EventFinalizerProc *finalizerProc); +JIM_EXPORT jim_wide Jim_DeleteTimeHandler (Jim_Interp *interp, jim_wide id); + +#define JIM_FILE_EVENTS 1 +#define JIM_TIME_EVENTS 2 +#define JIM_ALL_EVENTS (JIM_FILE_EVENTS|JIM_TIME_EVENTS) +#define JIM_DONT_WAIT 4 + +JIM_EXPORT int Jim_ProcessEvents (Jim_Interp *interp, int flags); +JIM_EXPORT int Jim_EvalObjBackground (Jim_Interp *interp, Jim_Obj *scriptObjPtr); + +int Jim_eventloopInit(Jim_Interp *interp); + +#endif /* __JIM_EVENTLOOP_H__ */ diff --git a/release/src/router/usbmodeswitch/jim/jim-exec.c b/release/src/router/usbmodeswitch/jim/jim-exec.c new file mode 100644 index 0000000000..90881564a4 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-exec.c @@ -0,0 +1,1619 @@ +/* + * (c) 2008 Steve Bennett + * + * Implements the exec command for Jim + * + * Based on code originally from Tcl 6.7 by John Ousterhout. + * From that code: + * + * The Tcl_Fork and Tcl_WaitPids procedures are based on code + * contributed by Karl Lehenbauer, Mark Diekhans and Peter + * da Silva. + * + * Copyright 1987-1991 Regents of the University of California + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +#include +#include + +#include "jim.h" +#include "jimautoconf.h" + +#if (!defined(HAVE_VFORK) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__) +/* Poor man's implementation of exec with system() + * The system() call *may* do command line redirection, etc. + * The standard output is not available. + * Can't redirect filehandles. + */ +static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp); + int i, j; + int rc; + + /* Create a quoted command line */ + for (i = 1; i < argc; i++) { + int len; + const char *arg = Jim_GetString(argv[i], &len); + + if (i > 1) { + Jim_AppendString(interp, cmdlineObj, " ", 1); + } + if (strpbrk(arg, "\\\" ") == NULL) { + /* No quoting required */ + Jim_AppendString(interp, cmdlineObj, arg, len); + continue; + } + + Jim_AppendString(interp, cmdlineObj, "\"", 1); + for (j = 0; j < len; j++) { + if (arg[j] == '\\' || arg[j] == '"') { + Jim_AppendString(interp, cmdlineObj, "\\", 1); + } + Jim_AppendString(interp, cmdlineObj, &arg[j], 1); + } + Jim_AppendString(interp, cmdlineObj, "\"", 1); + } + rc = system(Jim_String(cmdlineObj)); + + Jim_FreeNewObj(interp, cmdlineObj); + + if (rc) { + Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0); + Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1)); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0)); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc)); + Jim_SetGlobalVariableStr(interp, "errorCode", errorCode); + return JIM_ERR; + } + + return JIM_OK; +} + +int Jim_execInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG)) + return JIM_ERR; + Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL); + return JIM_OK; +} +#else +/* Full exec implementation for unix and mingw */ + +#include +#include + +#define XXX printf("@%s:%d\n", __FILE__, __LINE__); fflush(stdout); + +#if defined(__MINGW32__) + /* XXX: Should we use this implementation for cygwin too? */ + #include + + typedef HANDLE fdtype; + typedef HANDLE pidtype; + #define JIM_BAD_FD INVALID_HANDLE_VALUE + #define JIM_BAD_PID INVALID_HANDLE_VALUE + #define JimCloseFd CloseHandle + + #define WIFEXITED(STATUS) 1 + #define WEXITSTATUS(STATUS) (STATUS) + #define WIFSIGNALED(STATUS) 0 + #define WTERMSIG(STATUS) 0 + #define WNOHANG 1 + + static fdtype JimFileno(FILE *fh); + static pidtype JimWaitPid(pidtype pid, int *status, int nohang); + static fdtype JimDupFd(fdtype infd); + static fdtype JimOpenForRead(const char *filename); + static FILE *JimFdOpenForRead(fdtype fd); + static int JimPipe(fdtype pipefd[2]); + static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, + fdtype inputId, fdtype outputId, fdtype errorId); + static int JimErrno(void); +#else + #include "jim-signal.h" + #include + #include + #include + + typedef int fdtype; + typedef int pidtype; + #define JimPipe pipe + #define JimErrno() errno + #define JIM_BAD_FD -1 + #define JIM_BAD_PID -1 + #define JimFileno fileno + #define JimReadFd read + #define JimCloseFd close + #define JimWaitPid waitpid + #define JimDupFd dup + #define JimFdOpenForRead(FD) fdopen((FD), "r") + #define JimOpenForRead(NAME) open((NAME), O_RDONLY, 0) +#endif + +static const char *JimStrError(void); +static char **JimSaveEnv(char **env); +static void JimRestoreEnv(char **env); +static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, + pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr); +static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr); +static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId); +static fdtype JimCreateTemp(Jim_Interp *interp, const char *contents); +static fdtype JimOpenForWrite(const char *filename, int append); +static int JimRewindFd(fdtype fd); + +static void Jim_SetResultErrno(Jim_Interp *interp, const char *msg) +{ + Jim_SetResultFormatted(interp, "%s: %s", msg, JimStrError()); +} + +static const char *JimStrError(void) +{ + return strerror(JimErrno()); +} + +static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr) +{ + int len; + const char *s = Jim_GetString(objPtr, &len); + + if (len > 0 && s[len - 1] == '\n') { + objPtr->length--; + objPtr->bytes[objPtr->length] = '\0'; + } +} + +/** + * Read from 'fd', append the data to strObj and close 'fd'. + * Returns JIM_OK if OK, or JIM_ERR on error. + */ +static int JimAppendStreamToString(Jim_Interp *interp, fdtype fd, Jim_Obj *strObj) +{ + char buf[256]; + FILE *fh = JimFdOpenForRead(fd); + if (fh == NULL) { + return JIM_ERR; + } + + while (1) { + int retval = fread(buf, 1, sizeof(buf), fh); + if (retval > 0) { + Jim_AppendString(interp, strObj, buf, retval); + } + if (retval != sizeof(buf)) { + break; + } + } + Jim_RemoveTrailingNewline(strObj); + fclose(fh); + return JIM_OK; +} + +/* + * If the last character of the result is a newline, then remove + * the newline character (the newline would just confuse things). + * + * Note: Ideally we could do this by just reducing the length of stringrep + * by 1, but there is no API for this :-( + */ +static void JimTrimTrailingNewline(Jim_Interp *interp) +{ + int len; + const char *p = Jim_GetString(Jim_GetResult(interp), &len); + + if (len > 0 && p[len - 1] == '\n') { + Jim_SetResultString(interp, p, len - 1); + } +} + +/** + * Builds the environment array from $::env + * + * If $::env is not set, simply returns environ. + * + * Otherwise allocates the environ array from the contents of $::env + * + * If the exec fails, memory can be freed via JimFreeEnv() + */ +static char **JimBuildEnv(Jim_Interp *interp) +{ +#if defined(jim_ext_tclcompat) + int i; + int size; + int num; + int n; + char **envptr; + char *envdata; + + Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE); + + if (!objPtr) { + return Jim_GetEnviron(); + } + + /* We build the array as a single block consisting of the pointers followed by + * the strings. This has the advantage of being easy to allocate/free and being + * compatible with both unix and windows + */ + + /* Calculate the required size */ + num = Jim_ListLength(interp, objPtr); + if (num % 2) { + num--; + } + size = Jim_Length(objPtr); + /* We need one \0 and one equal sign for each element. + * A list has at least one space for each element except the first. + * We only need one extra char for the extra null terminator. + */ + size++; + + envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size); + envdata = (char *)&envptr[num / 2 + 1]; + + n = 0; + for (i = 0; i < num; i += 2) { + const char *s1, *s2; + Jim_Obj *elemObj; + + Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE); + s1 = Jim_String(elemObj); + Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE); + s2 = Jim_String(elemObj); + + envptr[n] = envdata; + envdata += sprintf(envdata, "%s=%s", s1, s2); + envdata++; + n++; + } + envptr[n] = NULL; + *envdata = 0; + + return envptr; +#else + return Jim_GetEnviron(); +#endif +} + +/** + * Frees the environment allocated by JimBuildEnv() + * + * Must pass original_environ. + */ +static void JimFreeEnv(char **env, char **original_environ) +{ +#ifdef jim_ext_tclcompat + if (env != original_environ) { + Jim_Free(env); + } +#endif +} + +/* + * Create error messages for unusual process exits. An + * extra newline gets appended to each error message, but + * it gets removed below (in the same fashion that an + * extra newline in the command's output is removed). + */ +static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus) +{ + Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0); + int rc = JIM_ERR; + + if (WIFEXITED(waitStatus)) { + if (WEXITSTATUS(waitStatus) == 0) { + Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1)); + rc = JIM_OK; + } + else { + Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1)); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid)); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus))); + } + } + else { + const char *type; + const char *action; + + if (WIFSIGNALED(waitStatus)) { + type = "CHILDKILLED"; + action = "killed"; + } + else { + type = "CHILDSUSP"; + action = "suspended"; + } + + Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1)); + +#ifdef jim_ext_signal + Jim_SetResultFormatted(interp, "child %s by signal %s", action, Jim_SignalId(WTERMSIG(waitStatus))); + Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalId(WTERMSIG(waitStatus)), -1)); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid)); + Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalName(WTERMSIG(waitStatus)), -1)); +#else + Jim_SetResultFormatted(interp, "child %s by signal %d", action, WTERMSIG(waitStatus)); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus))); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid)); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus))); +#endif + } + Jim_SetGlobalVariableStr(interp, "errorCode", errorCode); + return rc; +} + +/* + * Data structures of the following type are used by JimFork and + * JimWaitPids to keep track of child processes. + */ + +struct WaitInfo +{ + pidtype pid; /* Process id of child. */ + int status; /* Status returned when child exited or suspended. */ + int flags; /* Various flag bits; see below for definitions. */ +}; + +struct WaitInfoTable { + struct WaitInfo *info; + int size; + int used; +}; + +/* + * Flag bits in WaitInfo structures: + * + * WI_DETACHED - Non-zero means no-one cares about the + * process anymore. Ignore it until it + * exits, then forget about it. + */ + +#define WI_DETACHED 2 + +#define WAIT_TABLE_GROW_BY 4 + +static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData) +{ + struct WaitInfoTable *table = privData; + + Jim_Free(table->info); + Jim_Free(table); +} + +static struct WaitInfoTable *JimAllocWaitInfoTable(void) +{ + struct WaitInfoTable *table = Jim_Alloc(sizeof(*table)); + table->info = NULL; + table->size = table->used = 0; + + return table; +} + +/* + * The main [exec] command + */ +static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + fdtype outputId; /* File id for output pipe. -1 + * means command overrode. */ + fdtype errorId; /* File id for temporary file + * containing error output. */ + pidtype *pidPtr; + int numPids, result; + + /* + * See if the command is to be run in background; if so, create + * the command, detach it, and return. + */ + if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) { + Jim_Obj *listObj; + int i; + + argc--; + numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL); + if (numPids < 0) { + return JIM_ERR; + } + /* The return value is a list of the pids */ + listObj = Jim_NewListObj(interp, NULL, 0); + for (i = 0; i < numPids; i++) { + Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i])); + } + Jim_SetResult(interp, listObj); + JimDetachPids(interp, numPids, pidPtr); + Jim_Free(pidPtr); + return JIM_OK; + } + + /* + * Create the command's pipeline. + */ + numPids = + JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId); + + if (numPids < 0) { + return JIM_ERR; + } + + /* + * Read the child's output (if any) and put it into the result. + */ + Jim_SetResultString(interp, "", 0); + + result = JIM_OK; + if (outputId != JIM_BAD_FD) { + result = JimAppendStreamToString(interp, outputId, Jim_GetResult(interp)); + if (result < 0) { + Jim_SetResultErrno(interp, "error reading from output pipe"); + } + } + + if (JimCleanupChildren(interp, numPids, pidPtr, errorId) != JIM_OK) { + result = JIM_ERR; + } + return result; +} + +static void JimReapDetachedPids(struct WaitInfoTable *table) +{ + struct WaitInfo *waitPtr; + int count; + + if (!table) { + return; + } + + for (waitPtr = table->info, count = table->used; count > 0; waitPtr++, count--) { + if (waitPtr->flags & WI_DETACHED) { + int status; + pidtype pid = JimWaitPid(waitPtr->pid, &status, WNOHANG); + if (pid != JIM_BAD_PID) { + if (waitPtr != &table->info[table->used - 1]) { + *waitPtr = table->info[table->used - 1]; + } + table->used--; + } + } + } +} + +/** + * Does waitpid() on the given pid, and then removes the + * entry from the wait table. + * + * Returns the pid if OK and updates *statusPtr with the status, + * or JIM_BAD_PID if the pid was not in the table. + */ +static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr) +{ + int i; + + /* Find it in the table */ + for (i = 0; i < table->used; i++) { + if (pid == table->info[i].pid) { + /* wait for it */ + JimWaitPid(pid, statusPtr, 0); + + /* Remove it from the table */ + if (i != table->used - 1) { + table->info[i] = table->info[table->used - 1]; + } + table->used--; + return pid; + } + } + + /* Not found */ + return JIM_BAD_PID; +} + +/* + *---------------------------------------------------------------------- + * + * JimDetachPids -- + * + * This procedure is called to indicate that one or more child + * processes have been placed in background and are no longer + * cared about. These children can be cleaned up with JimReapDetachedPids(). + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr) +{ + int j; + struct WaitInfoTable *table = Jim_CmdPrivData(interp); + + for (j = 0; j < numPids; j++) { + /* Find it in the table */ + int i; + for (i = 0; i < table->used; i++) { + if (pidPtr[j] == table->info[i].pid) { + table->info[i].flags |= WI_DETACHED; + break; + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * JimCreatePipeline -- + * + * Given an argc/argv array, instantiate a pipeline of processes + * as described by the argv. + * + * Results: + * The return value is a count of the number of new processes + * created, or -1 if an error occurred while creating the pipeline. + * *pidArrayPtr is filled in with the address of a dynamically + * allocated array giving the ids of all of the processes. It + * is up to the caller to free this array when it isn't needed + * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in + * with the file id for the input pipe for the pipeline (if any): + * the caller must eventually close this file. If outPipePtr + * isn't NULL, then *outPipePtr is filled in with the file id + * for the output pipe from the pipeline: the caller must close + * this file. If errFilePtr isn't NULL, then *errFilePtr is filled + * with a file id that may be used to read error output after the + * pipeline completes. + * + * Side effects: + * Processes and pipes are created. + * + *---------------------------------------------------------------------- + */ +static int +JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr, + fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr) +{ + pidtype *pidPtr = NULL; /* Points to malloc-ed array holding all + * the pids of child processes. */ + int numPids = 0; /* Actual number of processes that exist + * at *pidPtr right now. */ + int cmdCount; /* Count of number of distinct commands + * found in argc/argv. */ + const char *input = NULL; /* Describes input for pipeline, depending + * on "inputFile". NULL means take input + * from stdin/pipe. */ + +#define FILE_NAME 0 /* input/output: filename */ +#define FILE_APPEND 1 /* output only: filename, append */ +#define FILE_HANDLE 2 /* input/output: filehandle */ +#define FILE_TEXT 3 /* input only: input is actual text */ + + int inputFile = FILE_NAME; /* 1 means input is name of input file. + * 2 means input is filehandle name. + * 0 means input holds actual + * text to be input to command. */ + + int outputFile = FILE_NAME; /* 0 means output is the name of output file. + * 1 means output is the name of output file, and append. + * 2 means output is filehandle name. + * All this is ignored if output is NULL + */ + int errorFile = FILE_NAME; /* 0 means error is the name of error file. + * 1 means error is the name of error file, and append. + * 2 means error is filehandle name. + * All this is ignored if error is NULL + */ + const char *output = NULL; /* Holds name of output file to pipe to, + * or NULL if output goes to stdout/pipe. */ + const char *error = NULL; /* Holds name of stderr file to pipe to, + * or NULL if stderr goes to stderr/pipe. */ + fdtype inputId = JIM_BAD_FD; + /* Readable file id input to current command in + * pipeline (could be file or pipe). JIM_BAD_FD + * means use stdin. */ + fdtype outputId = JIM_BAD_FD; + /* Writable file id for output from current + * command in pipeline (could be file or pipe). + * JIM_BAD_FD means use stdout. */ + fdtype errorId = JIM_BAD_FD; + /* Writable file id for all standard error + * output from all commands in pipeline. JIM_BAD_FD + * means use stderr. */ + fdtype lastOutputId = JIM_BAD_FD; + /* Write file id for output from last command + * in pipeline (could be file or pipe). + * -1 means use stdout. */ + fdtype pipeIds[2]; /* File ids for pipe that's being created. */ + int firstArg, lastArg; /* Indexes of first and last arguments in + * current command. */ + int lastBar; + int i; + pidtype pid; + char **save_environ; + struct WaitInfoTable *table = Jim_CmdPrivData(interp); + + /* Holds the args which will be used to exec */ + char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1)); + int arg_count = 0; + + JimReapDetachedPids(table); + + if (inPipePtr != NULL) { + *inPipePtr = JIM_BAD_FD; + } + if (outPipePtr != NULL) { + *outPipePtr = JIM_BAD_FD; + } + if (errFilePtr != NULL) { + *errFilePtr = JIM_BAD_FD; + } + pipeIds[0] = pipeIds[1] = JIM_BAD_FD; + + /* + * First, scan through all the arguments to figure out the structure + * of the pipeline. Count the number of distinct processes (it's the + * number of "|" arguments). If there are "<", "<<", or ">" arguments + * then make note of input and output redirection and remove these + * arguments and the arguments that follow them. + */ + cmdCount = 1; + lastBar = -1; + for (i = 0; i < argc; i++) { + const char *arg = Jim_String(argv[i]); + + if (arg[0] == '<') { + inputFile = FILE_NAME; + input = arg + 1; + if (*input == '<') { + inputFile = FILE_TEXT; + input++; + } + else if (*input == '@') { + inputFile = FILE_HANDLE; + input++; + } + + if (!*input && ++i < argc) { + input = Jim_String(argv[i]); + } + } + else if (arg[0] == '>') { + int dup_error = 0; + + outputFile = FILE_NAME; + + output = arg + 1; + if (*output == '>') { + outputFile = FILE_APPEND; + output++; + } + if (*output == '&') { + /* Redirect stderr too */ + output++; + dup_error = 1; + } + if (*output == '@') { + outputFile = FILE_HANDLE; + output++; + } + if (!*output && ++i < argc) { + output = Jim_String(argv[i]); + } + if (dup_error) { + errorFile = outputFile; + error = output; + } + } + else if (arg[0] == '2' && arg[1] == '>') { + error = arg + 2; + errorFile = FILE_NAME; + + if (*error == '@') { + errorFile = FILE_HANDLE; + error++; + } + else if (*error == '>') { + errorFile = FILE_APPEND; + error++; + } + if (!*error && ++i < argc) { + error = Jim_String(argv[i]); + } + } + else { + if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) { + if (i == lastBar + 1 || i == argc - 1) { + Jim_SetResultString(interp, "illegal use of | or |& in command", -1); + goto badargs; + } + lastBar = i; + cmdCount++; + } + /* Either |, |& or a "normal" arg, so store it in the arg array */ + arg_array[arg_count++] = (char *)arg; + continue; + } + + if (i >= argc) { + Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg); + goto badargs; + } + } + + if (arg_count == 0) { + Jim_SetResultString(interp, "didn't specify command to execute", -1); +badargs: + Jim_Free(arg_array); + return -1; + } + + /* Must do this before vfork(), so do it now */ + save_environ = JimSaveEnv(JimBuildEnv(interp)); + + /* + * Set up the redirected input source for the pipeline, if + * so requested. + */ + if (input != NULL) { + if (inputFile == FILE_TEXT) { + /* + * Immediate data in command. Create temporary file and + * put data into file. + */ + inputId = JimCreateTemp(interp, input); + if (inputId == JIM_BAD_FD) { + goto error; + } + } + else if (inputFile == FILE_HANDLE) { + /* Should be a file descriptor */ + Jim_Obj *fhObj = Jim_NewStringObj(interp, input, -1); + FILE *fh = Jim_AioFilehandle(interp, fhObj); + + Jim_FreeNewObj(interp, fhObj); + if (fh == NULL) { + goto error; + } + inputId = JimDupFd(JimFileno(fh)); + } + else { + /* + * File redirection. Just open the file. + */ + inputId = JimOpenForRead(input); + if (inputId == JIM_BAD_FD) { + Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, JimStrError()); + goto error; + } + } + } + else if (inPipePtr != NULL) { + if (JimPipe(pipeIds) != 0) { + Jim_SetResultErrno(interp, "couldn't create input pipe for command"); + goto error; + } + inputId = pipeIds[0]; + *inPipePtr = pipeIds[1]; + pipeIds[0] = pipeIds[1] = JIM_BAD_FD; + } + + /* + * Set up the redirected output sink for the pipeline from one + * of two places, if requested. + */ + if (output != NULL) { + if (outputFile == FILE_HANDLE) { + Jim_Obj *fhObj = Jim_NewStringObj(interp, output, -1); + FILE *fh = Jim_AioFilehandle(interp, fhObj); + + Jim_FreeNewObj(interp, fhObj); + if (fh == NULL) { + goto error; + } + fflush(fh); + lastOutputId = JimDupFd(JimFileno(fh)); + } + else { + /* + * Output is to go to a file. + */ + lastOutputId = JimOpenForWrite(output, outputFile == FILE_APPEND); + if (lastOutputId == JIM_BAD_FD) { + Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, JimStrError()); + goto error; + } + } + } + else if (outPipePtr != NULL) { + /* + * Output is to go to a pipe. + */ + if (JimPipe(pipeIds) != 0) { + Jim_SetResultErrno(interp, "couldn't create output pipe"); + goto error; + } + lastOutputId = pipeIds[1]; + *outPipePtr = pipeIds[0]; + pipeIds[0] = pipeIds[1] = JIM_BAD_FD; + } + /* If we are redirecting stderr with 2>filename or 2>@fileId, then we ignore errFilePtr */ + if (error != NULL) { + if (errorFile == FILE_HANDLE) { + if (strcmp(error, "1") == 0) { + /* Special 2>@1 */ + if (lastOutputId != JIM_BAD_FD) { + errorId = JimDupFd(lastOutputId); + } + else { + /* No redirection of stdout, so just use 2>@stdout */ + error = "stdout"; + } + } + if (errorId == JIM_BAD_FD) { + Jim_Obj *fhObj = Jim_NewStringObj(interp, error, -1); + FILE *fh = Jim_AioFilehandle(interp, fhObj); + + Jim_FreeNewObj(interp, fhObj); + if (fh == NULL) { + goto error; + } + fflush(fh); + errorId = JimDupFd(JimFileno(fh)); + } + } + else { + /* + * Output is to go to a file. + */ + errorId = JimOpenForWrite(error, errorFile == FILE_APPEND); + if (errorId == JIM_BAD_FD) { + Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError()); + goto error; + } + } + } + else if (errFilePtr != NULL) { + /* + * Set up the standard error output sink for the pipeline, if + * requested. Use a temporary file which is opened, then deleted. + * Could potentially just use pipe, but if it filled up it could + * cause the pipeline to deadlock: we'd be waiting for processes + * to complete before reading stderr, and processes couldn't complete + * because stderr was backed up. + */ + errorId = JimCreateTemp(interp, NULL); + if (errorId == JIM_BAD_FD) { + goto error; + } + *errFilePtr = JimDupFd(errorId); + } + + /* + * Scan through the argc array, forking off a process for each + * group of arguments between "|" arguments. + */ + + pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr)); + for (i = 0; i < numPids; i++) { + pidPtr[i] = JIM_BAD_PID; + } + for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) { + int pipe_dup_err = 0; + fdtype origErrorId = errorId; + + for (lastArg = firstArg; lastArg < arg_count; lastArg++) { + if (arg_array[lastArg][0] == '|') { + if (arg_array[lastArg][1] == '&') { + pipe_dup_err = 1; + } + break; + } + } + /* Replace | with NULL for execv() */ + arg_array[lastArg] = NULL; + if (lastArg == arg_count) { + outputId = lastOutputId; + } + else { + if (JimPipe(pipeIds) != 0) { + Jim_SetResultErrno(interp, "couldn't create pipe"); + goto error; + } + outputId = pipeIds[1]; + } + + /* Now fork the child */ + +#ifdef __MINGW32__ + pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ ? save_environ[0] : NULL, inputId, outputId, errorId); + if (pid == JIM_BAD_PID) { + Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]); + goto error; + } +#else + /* + * Disable SIGPIPE signals: if they were allowed, this process + * might go away unexpectedly if children misbehave. This code + * can potentially interfere with other application code that + * expects to handle SIGPIPEs; what's really needed is an + * arbiter for signals to allow them to be "shared". + */ + if (table->info == NULL) { + (void)signal(SIGPIPE, SIG_IGN); + } + + /* Need to do this befor vfork() */ + if (pipe_dup_err) { + errorId = outputId; + } + + /* + * Make a new process and enter it into the table if the fork + * is successful. + */ + pid = vfork(); + if (pid < 0) { + Jim_SetResultErrno(interp, "couldn't fork child process"); + goto error; + } + if (pid == 0) { + /* Child */ + + if (inputId != -1) dup2(inputId, 0); + if (outputId != -1) dup2(outputId, 1); + if (errorId != -1) dup2(errorId, 2); + + for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) { + close(i); + } + + execvp(arg_array[firstArg], &arg_array[firstArg]); + + /* Need to prep an error message before vfork(), just in case */ + fprintf(stderr, "couldn't exec \"%s\"", arg_array[firstArg]); + _exit(127); + } +#endif + + /* parent */ + + /* + * Enlarge the wait table if there isn't enough space for a new + * entry. + */ + if (table->used == table->size) { + table->size += WAIT_TABLE_GROW_BY; + table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info)); + } + + table->info[table->used].pid = pid; + table->info[table->used].flags = 0; + table->used++; + + pidPtr[numPids] = pid; + + /* Restore in case of pipe_dup_err */ + errorId = origErrorId; + + /* + * Close off our copies of file descriptors that were set up for + * this child, then set up the input for the next child. + */ + + if (inputId != JIM_BAD_FD) { + JimCloseFd(inputId); + } + if (outputId != JIM_BAD_FD) { + JimCloseFd(outputId); + } + inputId = pipeIds[0]; + pipeIds[0] = pipeIds[1] = JIM_BAD_FD; + } + *pidArrayPtr = pidPtr; + + /* + * All done. Cleanup open files lying around and then return. + */ + + cleanup: + if (inputId != JIM_BAD_FD) { + JimCloseFd(inputId); + } + if (lastOutputId != JIM_BAD_FD) { + JimCloseFd(lastOutputId); + } + if (errorId != JIM_BAD_FD) { + JimCloseFd(errorId); + } + Jim_Free(arg_array); + + JimRestoreEnv(save_environ); + + return numPids; + + /* + * An error occurred. There could have been extra files open, such + * as pipes between children. Clean them all up. Detach any child + * processes that have been created. + */ + + error: + if ((inPipePtr != NULL) && (*inPipePtr != JIM_BAD_FD)) { + JimCloseFd(*inPipePtr); + *inPipePtr = JIM_BAD_FD; + } + if ((outPipePtr != NULL) && (*outPipePtr != JIM_BAD_FD)) { + JimCloseFd(*outPipePtr); + *outPipePtr = JIM_BAD_FD; + } + if ((errFilePtr != NULL) && (*errFilePtr != JIM_BAD_FD)) { + JimCloseFd(*errFilePtr); + *errFilePtr = JIM_BAD_FD; + } + if (pipeIds[0] != JIM_BAD_FD) { + JimCloseFd(pipeIds[0]); + } + if (pipeIds[1] != JIM_BAD_FD) { + JimCloseFd(pipeIds[1]); + } + if (pidPtr != NULL) { + for (i = 0; i < numPids; i++) { + if (pidPtr[i] != JIM_BAD_PID) { + JimDetachPids(interp, 1, &pidPtr[i]); + } + } + Jim_Free(pidPtr); + } + numPids = -1; + goto cleanup; +} + +/* + *---------------------------------------------------------------------- + * + * JimCleanupChildren -- + * + * This is a utility procedure used to wait for child processes + * to exit, record information about abnormal exits, and then + * collect any stderr output generated by them. + * + * Results: + * The return value is a standard Tcl result. If anything at + * weird happened with the child processes, JIM_ERROR is returned + * and a message is left in interp->result. + * + * Side effects: + * If the last character of interp->result is a newline, then it + * is removed. File errorId gets closed, and pidPtr is freed + * back to the storage allocator. + * + *---------------------------------------------------------------------- + */ + +static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId) +{ + struct WaitInfoTable *table = Jim_CmdPrivData(interp); + int result = JIM_OK; + int i; + + for (i = 0; i < numPids; i++) { + int waitStatus = 0; + if (JimWaitForProcess(table, pidPtr[i], &waitStatus) != JIM_BAD_PID) { + if (JimCheckWaitStatus(interp, pidPtr[i], waitStatus) != JIM_OK) { + result = JIM_ERR; + } + } + } + Jim_Free(pidPtr); + + /* + * Read the standard error file. If there's anything there, + * then add the file's contents to the result + * string. + */ + if (errorId != JIM_BAD_FD) { + JimRewindFd(errorId); + if (JimAppendStreamToString(interp, errorId, Jim_GetResult(interp)) != JIM_OK) { + result = JIM_ERR; + } + } + + JimTrimTrailingNewline(interp); + + return result; +} + +int Jim_execInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG)) + return JIM_ERR; + Jim_CreateCommand(interp, "exec", Jim_ExecCmd, JimAllocWaitInfoTable(), JimFreeWaitInfoTable); + return JIM_OK; +} + +#if defined(__MINGW32__) +/* Windows-specific (mingw) implementation */ + +static SECURITY_ATTRIBUTES *JimStdSecAttrs(void) +{ + static SECURITY_ATTRIBUTES secAtts; + + secAtts.nLength = sizeof(SECURITY_ATTRIBUTES); + secAtts.lpSecurityDescriptor = NULL; + secAtts.bInheritHandle = TRUE; + return &secAtts; +} + +static int JimErrno(void) +{ + switch (GetLastError()) { + case ERROR_FILE_NOT_FOUND: return ENOENT; + case ERROR_PATH_NOT_FOUND: return ENOENT; + case ERROR_TOO_MANY_OPEN_FILES: return EMFILE; + case ERROR_ACCESS_DENIED: return EACCES; + case ERROR_INVALID_HANDLE: return EBADF; + case ERROR_BAD_ENVIRONMENT: return E2BIG; + case ERROR_BAD_FORMAT: return ENOEXEC; + case ERROR_INVALID_ACCESS: return EACCES; + case ERROR_INVALID_DRIVE: return ENOENT; + case ERROR_CURRENT_DIRECTORY: return EACCES; + case ERROR_NOT_SAME_DEVICE: return EXDEV; + case ERROR_NO_MORE_FILES: return ENOENT; + case ERROR_WRITE_PROTECT: return EROFS; + case ERROR_BAD_UNIT: return ENXIO; + case ERROR_NOT_READY: return EBUSY; + case ERROR_BAD_COMMAND: return EIO; + case ERROR_CRC: return EIO; + case ERROR_BAD_LENGTH: return EIO; + case ERROR_SEEK: return EIO; + case ERROR_WRITE_FAULT: return EIO; + case ERROR_READ_FAULT: return EIO; + case ERROR_GEN_FAILURE: return EIO; + case ERROR_SHARING_VIOLATION: return EACCES; + case ERROR_LOCK_VIOLATION: return EACCES; + case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE; + case ERROR_HANDLE_DISK_FULL: return ENOSPC; + case ERROR_NOT_SUPPORTED: return ENODEV; + case ERROR_REM_NOT_LIST: return EBUSY; + case ERROR_DUP_NAME: return EEXIST; + case ERROR_BAD_NETPATH: return ENOENT; + case ERROR_NETWORK_BUSY: return EBUSY; + case ERROR_DEV_NOT_EXIST: return ENODEV; + case ERROR_TOO_MANY_CMDS: return EAGAIN; + case ERROR_ADAP_HDW_ERR: return EIO; + case ERROR_BAD_NET_RESP: return EIO; + case ERROR_UNEXP_NET_ERR: return EIO; + case ERROR_NETNAME_DELETED: return ENOENT; + case ERROR_NETWORK_ACCESS_DENIED: return EACCES; + case ERROR_BAD_DEV_TYPE: return ENODEV; + case ERROR_BAD_NET_NAME: return ENOENT; + case ERROR_TOO_MANY_NAMES: return ENFILE; + case ERROR_TOO_MANY_SESS: return EIO; + case ERROR_SHARING_PAUSED: return EAGAIN; + case ERROR_REDIR_PAUSED: return EAGAIN; + case ERROR_FILE_EXISTS: return EEXIST; + case ERROR_CANNOT_MAKE: return ENOSPC; + case ERROR_OUT_OF_STRUCTURES: return ENFILE; + case ERROR_ALREADY_ASSIGNED: return EEXIST; + case ERROR_INVALID_PASSWORD: return EPERM; + case ERROR_NET_WRITE_FAULT: return EIO; + case ERROR_NO_PROC_SLOTS: return EAGAIN; + case ERROR_DISK_CHANGE: return EXDEV; + case ERROR_BROKEN_PIPE: return EPIPE; + case ERROR_OPEN_FAILED: return ENOENT; + case ERROR_DISK_FULL: return ENOSPC; + case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE; + case ERROR_INVALID_TARGET_HANDLE: return EBADF; + case ERROR_INVALID_NAME: return ENOENT; + case ERROR_PROC_NOT_FOUND: return ESRCH; + case ERROR_WAIT_NO_CHILDREN: return ECHILD; + case ERROR_CHILD_NOT_COMPLETE: return ECHILD; + case ERROR_DIRECT_ACCESS_HANDLE: return EBADF; + case ERROR_SEEK_ON_DEVICE: return ESPIPE; + case ERROR_BUSY_DRIVE: return EAGAIN; + case ERROR_DIR_NOT_EMPTY: return EEXIST; + case ERROR_NOT_LOCKED: return EACCES; + case ERROR_BAD_PATHNAME: return ENOENT; + case ERROR_LOCK_FAILED: return EACCES; + case ERROR_ALREADY_EXISTS: return EEXIST; + case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG; + case ERROR_BAD_PIPE: return EPIPE; + case ERROR_PIPE_BUSY: return EAGAIN; + case ERROR_PIPE_NOT_CONNECTED: return EPIPE; + case ERROR_DIRECTORY: return ENOTDIR; + } + return EINVAL; +} + +static int JimPipe(fdtype pipefd[2]) +{ + if (CreatePipe(&pipefd[0], &pipefd[1], NULL, 0)) { + return 0; + } + return -1; +} + +static fdtype JimDupFd(fdtype infd) +{ + fdtype dupfd; + pidtype pid = GetCurrentProcess(); + + if (DuplicateHandle(pid, infd, pid, &dupfd, 0, TRUE, DUPLICATE_SAME_ACCESS)) { + return dupfd; + } + return JIM_BAD_FD; +} + +static int JimRewindFd(fdtype fd) +{ + return SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ? -1 : 0; +} + +#if 0 +static int JimReadFd(fdtype fd, char *buffer, size_t len) +{ + DWORD num; + + if (ReadFile(fd, buffer, len, &num, NULL)) { + return num; + } + if (GetLastError() == ERROR_HANDLE_EOF || GetLastError() == ERROR_BROKEN_PIPE) { + return 0; + } + return -1; +} +#endif + +static FILE *JimFdOpenForRead(fdtype fd) +{ + return _fdopen(_open_osfhandle((int)fd, _O_RDONLY | _O_TEXT), "r"); +} + +static fdtype JimFileno(FILE *fh) +{ + return (fdtype)_get_osfhandle(_fileno(fh)); +} + +static fdtype JimOpenForRead(const char *filename) +{ + return CreateFile(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + JimStdSecAttrs(), OPEN_EXISTING, 0, NULL); +} + +static fdtype JimOpenForWrite(const char *filename, int append) +{ + return CreateFile(filename, append ? FILE_APPEND_DATA : GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + JimStdSecAttrs(), append ? OPEN_ALWAYS : CREATE_ALWAYS, 0, (HANDLE) NULL); +} + +static FILE *JimFdOpenForWrite(fdtype fd) +{ + return _fdopen(_open_osfhandle((int)fd, _O_TEXT), "w"); +} + +static pidtype JimWaitPid(pidtype pid, int *status, int nohang) +{ + DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE); + if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) { + /* WAIT_TIMEOUT can only happend with WNOHANG */ + return JIM_BAD_PID; + } + GetExitCodeProcess(pid, &ret); + *status = ret; + CloseHandle(pid); + return pid; +} + +static HANDLE JimCreateTemp(Jim_Interp *interp, const char *contents) +{ + char name[MAX_PATH]; + HANDLE handle; + + if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, "JIM", 0, name)) { + return JIM_BAD_FD; + } + + handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, JimStdSecAttrs(), + CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, + NULL); + + if (handle == INVALID_HANDLE_VALUE) { + goto error; + } + + if (contents != NULL) { + /* Use fdopen() to get automatic text-mode translation */ + FILE *fh = JimFdOpenForWrite(JimDupFd(handle)); + if (fh == NULL) { + goto error; + } + + if (fwrite(contents, strlen(contents), 1, fh) != 1) { + fclose(fh); + goto error; + } + fseek(fh, 0, SEEK_SET); + fclose(fh); + } + return handle; + + error: + Jim_SetResultErrno(interp, "failed to create temp file"); + CloseHandle(handle); + DeleteFile(name); + return JIM_BAD_FD; +} + +static int +JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH]) +{ + int i; + static char extensions[][5] = {".exe", "", ".bat"}; + + for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) { + lstrcpyn(fullPath, originalName, MAX_PATH - 5); + lstrcat(fullPath, extensions[i]); + + if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) { + continue; + } + if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) { + continue; + } + return 0; + } + + return -1; +} + +static char **JimSaveEnv(char **env) +{ + return env; +} + +static void JimRestoreEnv(char **env) +{ + JimFreeEnv(env, NULL); +} + +static Jim_Obj * +JimWinBuildCommandLine(Jim_Interp *interp, char **argv) +{ + char *start, *special; + int quote, i; + + Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0); + + for (i = 0; argv[i]; i++) { + if (i > 0) { + Jim_AppendString(interp, strObj, " ", 1); + } + + if (argv[i][0] == '\0') { + quote = 1; + } + else { + quote = 0; + for (start = argv[i]; *start != '\0'; start++) { + if (isspace(UCHAR(*start))) { + quote = 1; + break; + } + } + } + if (quote) { + Jim_AppendString(interp, strObj, "\"" , 1); + } + + start = argv[i]; + for (special = argv[i]; ; ) { + if ((*special == '\\') && (special[1] == '\\' || + special[1] == '"' || (quote && special[1] == '\0'))) { + Jim_AppendString(interp, strObj, start, special - start); + start = special; + while (1) { + special++; + if (*special == '"' || (quote && *special == '\0')) { + /* + * N backslashes followed a quote -> insert + * N * 2 + 1 backslashes then a quote. + */ + + Jim_AppendString(interp, strObj, start, special - start); + break; + } + if (*special != '\\') { + break; + } + } + Jim_AppendString(interp, strObj, start, special - start); + start = special; + } + if (*special == '"') { + if (special == start) { + Jim_AppendString(interp, strObj, "\"", 1); + } + else { + Jim_AppendString(interp, strObj, start, special - start); + } + Jim_AppendString(interp, strObj, "\\\"", 2); + start = special + 1; + } + if (*special == '\0') { + break; + } + special++; + } + Jim_AppendString(interp, strObj, start, special - start); + if (quote) { + Jim_AppendString(interp, strObj, "\"", 1); + } + } + return strObj; +} + +static pidtype +JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, fdtype inputId, fdtype outputId, fdtype errorId) +{ + STARTUPINFO startInfo; + PROCESS_INFORMATION procInfo; + HANDLE hProcess, h; + char execPath[MAX_PATH]; + char *originalName; + pidtype pid = JIM_BAD_PID; + Jim_Obj *cmdLineObj; + + if (JimWinFindExecutable(argv[0], execPath) < 0) { + return JIM_BAD_PID; + } + originalName = argv[0]; + argv[0] = execPath; + + hProcess = GetCurrentProcess(); + cmdLineObj = JimWinBuildCommandLine(interp, argv); + + /* + * STARTF_USESTDHANDLES must be used to pass handles to child process. + * Using SetStdHandle() and/or dup2() only works when a console mode + * parent process is spawning an attached console mode child process. + */ + + ZeroMemory(&startInfo, sizeof(startInfo)); + startInfo.cb = sizeof(startInfo); + startInfo.dwFlags = STARTF_USESTDHANDLES; + startInfo.hStdInput = INVALID_HANDLE_VALUE; + startInfo.hStdOutput= INVALID_HANDLE_VALUE; + startInfo.hStdError = INVALID_HANDLE_VALUE; + + /* + * Duplicate all the handles which will be passed off as stdin, stdout + * and stderr of the child process. The duplicate handles are set to + * be inheritable, so the child process can use them. + */ + if (inputId == JIM_BAD_FD) { + if (CreatePipe(&startInfo.hStdInput, &h, JimStdSecAttrs(), 0) != FALSE) { + CloseHandle(h); + } + } else { + DuplicateHandle(hProcess, inputId, hProcess, &startInfo.hStdInput, + 0, TRUE, DUPLICATE_SAME_ACCESS); + } + if (startInfo.hStdInput == JIM_BAD_FD) { + goto end; + } + + if (outputId == JIM_BAD_FD) { + startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0, + JimStdSecAttrs(), OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } else { + DuplicateHandle(hProcess, outputId, hProcess, &startInfo.hStdOutput, + 0, TRUE, DUPLICATE_SAME_ACCESS); + } + if (startInfo.hStdOutput == JIM_BAD_FD) { + goto end; + } + + if (errorId == JIM_BAD_FD) { + /* + * If handle was not set, errors should be sent to an infinitely + * deep sink. + */ + + startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0, + JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } else { + DuplicateHandle(hProcess, errorId, hProcess, &startInfo.hStdError, + 0, TRUE, DUPLICATE_SAME_ACCESS); + } + if (startInfo.hStdError == JIM_BAD_FD) { + goto end; + } + + if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE, + 0, env, NULL, &startInfo, &procInfo)) { + goto end; + } + + /* + * "When an application spawns a process repeatedly, a new thread + * instance will be created for each process but the previous + * instances may not be cleaned up. This results in a significant + * virtual memory loss each time the process is spawned. If there + * is a WaitForInputIdle() call between CreateProcess() and + * CloseHandle(), the problem does not occur." PSS ID Number: Q124121 + */ + + WaitForInputIdle(procInfo.hProcess, 5000); + CloseHandle(procInfo.hThread); + + pid = procInfo.hProcess; + + end: + Jim_FreeNewObj(interp, cmdLineObj); + if (startInfo.hStdInput != JIM_BAD_FD) { + CloseHandle(startInfo.hStdInput); + } + if (startInfo.hStdOutput != JIM_BAD_FD) { + CloseHandle(startInfo.hStdOutput); + } + if (startInfo.hStdError != JIM_BAD_FD) { + CloseHandle(startInfo.hStdError); + } + return pid; +} +#else +/* Unix-specific implementation */ +static int JimOpenForWrite(const char *filename, int append) +{ + return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666); +} + +static int JimRewindFd(int fd) +{ + return lseek(fd, 0L, SEEK_SET); +} + +static int JimCreateTemp(Jim_Interp *interp, const char *contents) +{ + char inName[] = "/tmp/tcl.tmp.XXXXXX"; + + int fd = mkstemp(inName); + if (fd == JIM_BAD_FD) { + Jim_SetResultErrno(interp, "couldn't create temp file"); + return -1; + } + unlink(inName); + if (contents) { + int length = strlen(contents); + if (write(fd, contents, length) != length) { + Jim_SetResultErrno(interp, "couldn't write temp file"); + close(fd); + return -1; + } + lseek(fd, 0L, SEEK_SET); + } + return fd; +} + +static char **JimSaveEnv(char **env) +{ + char **saveenv = Jim_GetEnviron(); + Jim_SetEnviron(env); + return saveenv; +} + +static void JimRestoreEnv(char **env) +{ + JimFreeEnv(Jim_GetEnviron(), env); + Jim_SetEnviron(env); +} +#endif +#endif diff --git a/release/src/router/usbmodeswitch/jim/jim-file.c b/release/src/router/usbmodeswitch/jim/jim-file.c new file mode 100644 index 0000000000..7b483685ec --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-file.c @@ -0,0 +1,929 @@ +/* + * Implements the file command for jim + * + * (c) 2008 Steve Bennett + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + * + * Based on code originally from Tcl 6.7: + * + * Copyright 1987-1991 Regents of the University of California + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jim.h" +#include "jimautoconf.h" +#include "jim-subcmd.h" + +# ifndef MAXPATHLEN +# define MAXPATHLEN JIM_PATH_LEN +# endif + +/* + *---------------------------------------------------------------------- + * + * JimGetFileType -- + * + * Given a mode word, returns a string identifying the type of a + * file. + * + * Results: + * A static text string giving the file type from mode. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static const char *JimGetFileType(int mode) +{ + if (S_ISREG(mode)) { + return "file"; + } + else if (S_ISDIR(mode)) { + return "directory"; + } + else if (S_ISCHR(mode)) { + return "characterSpecial"; + } + else if (S_ISBLK(mode)) { + return "blockSpecial"; + } + else if (S_ISFIFO(mode)) { + return "fifo"; +#ifdef S_ISLNK + } + else if (S_ISLNK(mode)) { + return "link"; +#endif +#ifdef S_ISSOCK + } + else if (S_ISSOCK(mode)) { + return "socket"; +#endif + } + return "unknown"; +} + +/* + *---------------------------------------------------------------------- + * + * StoreStatData -- + * + * This is a utility procedure that breaks out the fields of a + * "stat" structure and stores them in textual form into the + * elements of an associative array. + * + * Results: + * Returns a standard Tcl return value. If an error occurs then + * a message is left in interp->result. + * + * Side effects: + * Elements of the associative array given by "varName" are modified. + * + *---------------------------------------------------------------------- + */ + +static int set_array_int_value(Jim_Interp *interp, Jim_Obj *container, const char *key, + jim_wide value) +{ + Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1); + Jim_Obj *valobj = Jim_NewWideObj(interp, value); + + if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj, JIM_ERRMSG) != JIM_OK) { + Jim_FreeObj(interp, nameobj); + Jim_FreeObj(interp, valobj); + return JIM_ERR; + } + return JIM_OK; +} + +static int set_array_string_value(Jim_Interp *interp, Jim_Obj *container, const char *key, + const char *value) +{ + Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1); + Jim_Obj *valobj = Jim_NewStringObj(interp, value, -1); + + if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj, JIM_ERRMSG) != JIM_OK) { + Jim_FreeObj(interp, nameobj); + Jim_FreeObj(interp, valobj); + return JIM_ERR; + } + return JIM_OK; +} + +static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb) +{ + if (set_array_int_value(interp, varName, "dev", sb->st_dev) != JIM_OK) { + Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variables isn't array", varName); + return JIM_ERR; + } + set_array_int_value(interp, varName, "ino", sb->st_ino); + set_array_int_value(interp, varName, "mode", sb->st_mode); + set_array_int_value(interp, varName, "nlink", sb->st_nlink); + set_array_int_value(interp, varName, "uid", sb->st_uid); + set_array_int_value(interp, varName, "gid", sb->st_gid); + set_array_int_value(interp, varName, "size", sb->st_size); + set_array_int_value(interp, varName, "atime", sb->st_atime); + set_array_int_value(interp, varName, "mtime", sb->st_mtime); + set_array_int_value(interp, varName, "ctime", sb->st_ctime); + set_array_string_value(interp, varName, "type", JimGetFileType((int)sb->st_mode)); + + /* And also return the value */ + Jim_SetResult(interp, Jim_GetVariable(interp, varName, 0)); + + return JIM_OK; +} + +static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *path = Jim_String(argv[0]); + const char *p = strrchr(path, '/'); + + if (!p) { + Jim_SetResultString(interp, ".", -1); + } + else if (p == path) { + Jim_SetResultString(interp, "/", -1); + } +#if defined(__MINGW32__) + else if (p[-1] == ':') { + /* z:/dir => z:/ */ + Jim_SetResultString(interp, path, p - path + 1); + } +#endif + else { + Jim_SetResultString(interp, path, p - path); + } + return JIM_OK; +} + +static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *path = Jim_String(argv[0]); + const char *lastSlash = strrchr(path, '/'); + const char *p = strrchr(path, '.'); + + if (p == NULL || (lastSlash != NULL && lastSlash > p)) { + Jim_SetResult(interp, argv[0]); + } + else { + Jim_SetResultString(interp, path, p - path); + } + return JIM_OK; +} + +static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *path = Jim_String(argv[0]); + const char *lastSlash = strrchr(path, '/'); + const char *p = strrchr(path, '.'); + + if (p == NULL || (lastSlash != NULL && lastSlash >= p)) { + p = ""; + } + Jim_SetResultString(interp, p, -1); + return JIM_OK; +} + +static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *path = Jim_String(argv[0]); + const char *lastSlash = strrchr(path, '/'); + + if (lastSlash) { + Jim_SetResultString(interp, lastSlash + 1, -1); + } + else { + Jim_SetResult(interp, argv[0]); + } + return JIM_OK; +} + +static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ +#ifdef HAVE_REALPATH + const char *path = Jim_String(argv[0]); + char *newname = Jim_Alloc(MAXPATHLEN + 1); + + if (realpath(path, newname)) { + Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1)); + } + else { + Jim_Free(newname); + Jim_SetResult(interp, argv[0]); + } + return JIM_OK; +#else + Jim_SetResultString(interp, "Not implemented", -1); + return JIM_ERR; +#endif +} + +static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int i; + char *newname = Jim_Alloc(MAXPATHLEN + 1); + char *last = newname; + + *newname = 0; + + /* Simple implementation for now */ + for (i = 0; i < argc; i++) { + int len; + const char *part = Jim_GetString(argv[i], &len); + + if (*part == '/') { + /* Absolute component, so go back to the start */ + last = newname; + } +#if defined(__MINGW32__) + else if (strchr(part, ':')) { + /* Absolute compontent on mingw, so go back to the start */ + last = newname; + } +#endif + else if (part[0] == '.') { + if (part[1] == '/') { + part += 2; + len -= 2; + } + else if (part[1] == 0 && last != newname) { + /* Adding '.' to an existing path does nothing */ + continue; + } + } + + /* Add a slash if needed */ + if (last != newname && last[-1] != '/') { + *last++ = '/'; + } + + if (len) { + if (last + len - newname >= MAXPATHLEN) { + Jim_Free(newname); + Jim_SetResultString(interp, "Path too long", -1); + return JIM_ERR; + } + memcpy(last, part, len); + last += len; + } + + /* Remove a slash if needed */ + if (last > newname + 1 && last[-1] == '/') { + *--last = 0; + } + } + + *last = 0; + + /* Probably need to handle some special cases ... */ + + Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname)); + + return JIM_OK; +} + +static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode) +{ + const char *path = Jim_String(filename); + int rc = access(path, mode); + + Jim_SetResultBool(interp, rc != -1); + + return JIM_OK; +} + +static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return file_access(interp, argv[0], R_OK); +} + +static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return file_access(interp, argv[0], W_OK); +} + +static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return file_access(interp, argv[0], X_OK); +} + +static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return file_access(interp, argv[0], F_OK); +} + +static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int force = Jim_CompareStringImmediate(interp, argv[0], "-force"); + + if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) { + argc++; + argv--; + } + + while (argc--) { + const char *path = Jim_String(argv[0]); + + if (unlink(path) == -1 && errno != ENOENT) { + if (rmdir(path) == -1) { + /* Maybe try using the script helper */ + if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) { + Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path, + strerror(errno)); + return JIM_ERR; + } + } + } + argv++; + } + return JIM_OK; +} + +#ifdef HAVE_MKDIR_ONE_ARG +#define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME) +#else +#define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755) +#endif + +/** + * Create directory, creating all intermediate paths if necessary. + * + * Returns 0 if OK or -1 on failure (and sets errno) + * + * Note: The path may be modified. + */ +static int mkdir_all(char *path) +{ + int ok = 1; + + /* First time just try to make the dir */ + goto first; + + while (ok--) { + /* Must have failed the first time, so recursively make the parent and try again */ + char *slash = strrchr(path, '/'); + + if (slash && slash != path) { + *slash = 0; + if (mkdir_all(path) != 0) { + return -1; + } + *slash = '/'; + } + first: + if (MKDIR_DEFAULT(path) == 0) { + return 0; + } + if (errno == ENOENT) { + /* Create the parent and try again */ + continue; + } + /* Maybe it already exists as a directory */ + if (errno == EEXIST) { + struct stat sb; + + if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { + return 0; + } + /* Restore errno */ + errno = EEXIST; + } + /* Failed */ + break; + } + return -1; +} + +static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + while (argc--) { + char *path = Jim_StrDup(Jim_String(argv[0])); + int rc = mkdir_all(path); + + Jim_Free(path); + if (rc != 0) { + Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0], + strerror(errno)); + return JIM_ERR; + } + argv++; + } + return JIM_OK; +} + +#ifdef HAVE_MKSTEMP +static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int fd; + char *filename; + const char *template = "/tmp/tcl.tmp.XXXXXX"; + + if (argc >= 1) { + template = Jim_String(argv[0]); + } + filename = Jim_StrDup(template); + + fd = mkstemp(filename); + if (fd < 0) { + Jim_SetResultString(interp, "Failed to create tempfile", -1); + return JIM_ERR; + } + close(fd); + + Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, filename, -1)); + return JIM_OK; +} +#endif + +static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *source; + const char *dest; + int force = 0; + + if (argc == 3) { + if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) { + return -1; + } + force++; + argv++; + argc--; + } + + source = Jim_String(argv[0]); + dest = Jim_String(argv[1]); + + if (!force && access(dest, F_OK) == 0) { + Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0], + argv[1]); + return JIM_ERR; + } + + if (rename(source, dest) != 0) { + Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1], + strerror(errno)); + return JIM_ERR; + } + + return JIM_OK; +} + +static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb) +{ + const char *path = Jim_String(filename); + + if (stat(path, sb) == -1) { + Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno)); + return JIM_ERR; + } + return JIM_OK; +} + +#ifndef HAVE_LSTAT +#define lstat stat +#endif + +static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb) +{ + const char *path = Jim_String(filename); + + if (lstat(path, sb) == -1) { + Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno)); + return JIM_ERR; + } + return JIM_OK; +} + +static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + + if (file_stat(interp, argv[0], &sb) != JIM_OK) { + return JIM_ERR; + } + Jim_SetResultInt(interp, sb.st_atime); + return JIM_OK; +} + +static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + + if (argc == 2) { +#ifdef HAVE_UTIMES + jim_wide newtime; + struct timeval times[2]; + + if (Jim_GetWide(interp, argv[1], &newtime) != JIM_OK) { + return JIM_ERR; + } + + times[1].tv_sec = times[0].tv_sec = newtime; + times[1].tv_usec = times[0].tv_usec = 0; + + if (utimes(Jim_String(argv[0]), times) != 0) { + Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno)); + return JIM_ERR; + } +#else + Jim_SetResultString(interp, "Not implemented", -1); + return JIM_ERR; +#endif + } + if (file_stat(interp, argv[0], &sb) != JIM_OK) { + return JIM_ERR; + } + Jim_SetResultInt(interp, sb.st_mtime); + return JIM_OK; +} + +static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return Jim_EvalPrefix(interp, "file copy", argc, argv); +} + +static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + + if (file_stat(interp, argv[0], &sb) != JIM_OK) { + return JIM_ERR; + } + Jim_SetResultInt(interp, sb.st_size); + return JIM_OK; +} + +static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + int ret = 0; + + if (file_stat(interp, argv[0], &sb) == JIM_OK) { + ret = S_ISDIR(sb.st_mode); + } + Jim_SetResultInt(interp, ret); + return JIM_OK; +} + +static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + int ret = 0; + + if (file_stat(interp, argv[0], &sb) == JIM_OK) { + ret = S_ISREG(sb.st_mode); + } + Jim_SetResultInt(interp, ret); + return JIM_OK; +} + +#ifdef HAVE_GETEUID +static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + int ret = 0; + + if (file_stat(interp, argv[0], &sb) == JIM_OK) { + ret = (geteuid() == sb.st_uid); + } + Jim_SetResultInt(interp, ret); + return JIM_OK; +} +#endif + +#if defined(HAVE_READLINK) +static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *path = Jim_String(argv[0]); + char *linkValue = Jim_Alloc(MAXPATHLEN + 1); + + int linkLength = readlink(path, linkValue, MAXPATHLEN); + + if (linkLength == -1) { + Jim_Free(linkValue); + Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno)); + return JIM_ERR; + } + linkValue[linkLength] = 0; + Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength)); + return JIM_OK; +} +#endif + +static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + + if (file_lstat(interp, argv[0], &sb) != JIM_OK) { + return JIM_ERR; + } + Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1); + return JIM_OK; +} + +static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + + if (file_lstat(interp, argv[0], &sb) != JIM_OK) { + return JIM_ERR; + } + return StoreStatData(interp, argv[1], &sb); +} + +static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + + if (file_stat(interp, argv[0], &sb) != JIM_OK) { + return JIM_ERR; + } + return StoreStatData(interp, argv[1], &sb); +} + +static const jim_subcmd_type file_command_table[] = { + { .cmd = "atime", + .args = "name", + .function = file_cmd_atime, + .minargs = 1, + .maxargs = 1, + .description = "Last access time" + }, + { .cmd = "mtime", + .args = "name ?time?", + .function = file_cmd_mtime, + .minargs = 1, + .maxargs = 2, + .description = "Get or set last modification time" + }, + { .cmd = "copy", + .args = "?-force? source dest", + .function = file_cmd_copy, + .minargs = 2, + .maxargs = 3, + .description = "Copy source file to destination file" + }, + { .cmd = "dirname", + .args = "name", + .function = file_cmd_dirname, + .minargs = 1, + .maxargs = 1, + .description = "Directory part of the name" + }, + { .cmd = "rootname", + .args = "name", + .function = file_cmd_rootname, + .minargs = 1, + .maxargs = 1, + .description = "Name without any extension" + }, + { .cmd = "extension", + .args = "name", + .function = file_cmd_extension, + .minargs = 1, + .maxargs = 1, + .description = "Last extension including the dot" + }, + { .cmd = "tail", + .args = "name", + .function = file_cmd_tail, + .minargs = 1, + .maxargs = 1, + .description = "Last component of the name" + }, + { .cmd = "normalize", + .args = "name", + .function = file_cmd_normalize, + .minargs = 1, + .maxargs = 1, + .description = "Normalized path of name" + }, + { .cmd = "join", + .args = "name ?name ...?", + .function = file_cmd_join, + .minargs = 1, + .maxargs = -1, + .description = "Join multiple path components" + }, + { .cmd = "readable", + .args = "name", + .function = file_cmd_readable, + .minargs = 1, + .maxargs = 1, + .description = "Is file readable" + }, + { .cmd = "writable", + .args = "name", + .function = file_cmd_writable, + .minargs = 1, + .maxargs = 1, + .description = "Is file writable" + }, + { .cmd = "executable", + .args = "name", + .function = file_cmd_executable, + .minargs = 1, + .maxargs = 1, + .description = "Is file executable" + }, + { .cmd = "exists", + .args = "name", + .function = file_cmd_exists, + .minargs = 1, + .maxargs = 1, + .description = "Does file exist" + }, + { .cmd = "delete", + .args = "?-force|--? name ...", + .function = file_cmd_delete, + .minargs = 1, + .maxargs = -1, + .description = "Deletes the files or directories (must be empty unless -force)" + }, + { .cmd = "mkdir", + .args = "dir ...", + .function = file_cmd_mkdir, + .minargs = 1, + .maxargs = -1, + .description = "Creates the directories" + }, +#ifdef HAVE_MKSTEMP + { .cmd = "tempfile", + .args = "?template?", + .function = file_cmd_tempfile, + .minargs = 0, + .maxargs = 1, + .description = "Creates a temporary filename" + }, +#endif + { .cmd = "rename", + .args = "?-force? source dest", + .function = file_cmd_rename, + .minargs = 2, + .maxargs = 3, + .description = "Renames a file" + }, +#if defined(HAVE_READLINK) + { .cmd = "readlink", + .args = "name", + .function = file_cmd_readlink, + .minargs = 1, + .maxargs = 1, + .description = "Value of the symbolic link" + }, +#endif + { .cmd = "size", + .args = "name", + .function = file_cmd_size, + .minargs = 1, + .maxargs = 1, + .description = "Size of file" + }, + { .cmd = "stat", + .args = "name var", + .function = file_cmd_stat, + .minargs = 2, + .maxargs = 2, + .description = "Stores results of stat in var array" + }, + { .cmd = "lstat", + .args = "name var", + .function = file_cmd_lstat, + .minargs = 2, + .maxargs = 2, + .description = "Stores results of lstat in var array" + }, + { .cmd = "type", + .args = "name", + .function = file_cmd_type, + .minargs = 1, + .maxargs = 1, + .description = "Returns type of the file" + }, +#ifdef HAVE_GETEUID + { .cmd = "owned", + .args = "name", + .function = file_cmd_owned, + .minargs = 1, + .maxargs = 1, + .description = "Returns 1 if owned by the current owner" + }, +#endif + { .cmd = "isdirectory", + .args = "name", + .function = file_cmd_isdirectory, + .minargs = 1, + .maxargs = 1, + .description = "Returns 1 if name is a directory" + }, + { .cmd = "isfile", + .args = "name", + .function = file_cmd_isfile, + .minargs = 1, + .maxargs = 1, + .description = "Returns 1 if name is a file" + }, + { + .cmd = 0 + } +}; + +static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *path; + + if (argc != 2) { + Jim_WrongNumArgs(interp, 1, argv, "dirname"); + return JIM_ERR; + } + + path = Jim_String(argv[1]); + + if (chdir(path) != 0) { + Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path, + strerror(errno)); + return JIM_ERR; + } + return JIM_OK; +} + +static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const int cwd_len = 2048; + char *cwd = malloc(cwd_len); + + if (getcwd(cwd, cwd_len) == NULL) { + Jim_SetResultString(interp, "Failed to get pwd", -1); + return JIM_ERR; + } +#if defined(__MINGW32__) + { + /* Try to keep backlashes out of paths */ + char *p = cwd; + while ((p = strchr(p, '\\')) != NULL) { + *p++ = '/'; + } + } +#endif + + Jim_SetResultString(interp, cwd, -1); + + free(cwd); + return JIM_OK; +} + +int Jim_fileInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG)) + return JIM_ERR; + + Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL); + Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL); + Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL); + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-format.c b/release/src/router/usbmodeswitch/jim/jim-format.c new file mode 100644 index 0000000000..c9cde89da5 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-format.c @@ -0,0 +1,433 @@ +/* + * Implements the internals of the format command for jim + * + * The FreeBSD license + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + * + * Based on code originally from Tcl 8.5: + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * Copyright (c) 1999 by Scriptics Corporation. + * + * See the file "tcl.license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ +#include +#include + +#include "jim.h" +#include "jimautoconf.h" +#include "utf8.h" + +#define JIM_UTF_MAX 3 +#define JIM_INTEGER_SPACE 24 +#define MAX_FLOAT_WIDTH 320 + +/** + * Apply the printf-like format in fmtObjPtr with the given arguments. + * + * Returns a new object with zero reference count if OK, or NULL on error. + */ +Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv) +{ + const char *span, *format, *formatEnd, *msg; + int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0; + static const char * const mixedXPG = + "cannot mix \"%\" and \"%n$\" conversion specifiers"; + static const char * const badIndex[2] = { + "not enough arguments for all format specifiers", + "\"%n$\" argument index out of range" + }; + int formatLen; + Jim_Obj *resultPtr; + + /* A single buffer is used to store numeric fields (with sprintf()) + * This buffer is allocated/reallocated as necessary + */ + char *num_buffer = NULL; + int num_buffer_size = 0; + + span = format = Jim_GetString(fmtObjPtr, &formatLen); + formatEnd = format + formatLen; + resultPtr = Jim_NewStringObj(interp, "", 0); + + while (format != formatEnd) { + char *end; + int gotMinus, sawFlag; + int gotPrecision, useShort; + long width, precision; + int newXpg; + int ch; + int step; + int doubleType; + char pad = ' '; + char spec[2*JIM_INTEGER_SPACE + 12]; + char *p; + + int formatted_chars; + int formatted_bytes; + const char *formatted_buf; + + step = utf8_tounicode(format, &ch); + format += step; + if (ch != '%') { + numBytes += step; + continue; + } + if (numBytes) { + Jim_AppendString(interp, resultPtr, span, numBytes); + numBytes = 0; + } + + /* + * Saw a % : process the format specifier. + * + * Step 0. Handle special case of escaped format marker (i.e., %%). + */ + + step = utf8_tounicode(format, &ch); + if (ch == '%') { + span = format; + numBytes = step; + format += step; + continue; + } + + /* + * Step 1. XPG3 position specifier + */ + + newXpg = 0; + if (isdigit(ch)) { + int position = strtoul(format, &end, 10); + if (*end == '$') { + newXpg = 1; + objIndex = position - 1; + format = end + 1; + step = utf8_tounicode(format, &ch); + } + } + if (newXpg) { + if (gotSequential) { + msg = mixedXPG; + goto errorMsg; + } + gotXpg = 1; + } else { + if (gotXpg) { + msg = mixedXPG; + goto errorMsg; + } + gotSequential = 1; + } + if ((objIndex < 0) || (objIndex >= objc)) { + msg = badIndex[gotXpg]; + goto errorMsg; + } + + /* + * Step 2. Set of flags. Also build up the sprintf spec. + */ + p = spec; + *p++ = '%'; + + gotMinus = 0; + sawFlag = 1; + do { + switch (ch) { + case '-': + gotMinus = 1; + break; + case '0': + pad = ch; + break; + case ' ': + case '+': + case '#': + break; + default: + sawFlag = 0; + continue; + } + *p++ = ch; + format += step; + step = utf8_tounicode(format, &ch); + } while (sawFlag); + + /* + * Step 3. Minimum field width. + */ + + width = 0; + if (isdigit(ch)) { + width = strtoul(format, &end, 10); + format = end; + step = utf8_tounicode(format, &ch); + } else if (ch == '*') { + if (objIndex >= objc - 1) { + msg = badIndex[gotXpg]; + goto errorMsg; + } + if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) { + goto error; + } + if (width < 0) { + width = -width; + if (!gotMinus) { + *p++ = '-'; + gotMinus = 1; + } + } + objIndex++; + format += step; + step = utf8_tounicode(format, &ch); + } + + /* + * Step 4. Precision. + */ + + gotPrecision = precision = 0; + if (ch == '.') { + gotPrecision = 1; + format += step; + step = utf8_tounicode(format, &ch); + } + if (isdigit(ch)) { + precision = strtoul(format, &end, 10); + format = end; + step = utf8_tounicode(format, &ch); + } else if (ch == '*') { + if (objIndex >= objc - 1) { + msg = badIndex[gotXpg]; + goto errorMsg; + } + if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) { + goto error; + } + + /* + * TODO: Check this truncation logic. + */ + + if (precision < 0) { + precision = 0; + } + objIndex++; + format += step; + step = utf8_tounicode(format, &ch); + } + + /* + * Step 5. Length modifier. + */ + + useShort = 0; + if (ch == 'h') { + useShort = 1; + format += step; + step = utf8_tounicode(format, &ch); + } else if (ch == 'l') { + /* Just for compatibility. All non-short integers are wide. */ + format += step; + step = utf8_tounicode(format, &ch); + if (ch == 'l') { + format += step; + step = utf8_tounicode(format, &ch); + } + } + + format += step; + span = format; + + /* + * Step 6. The actual conversion character. + */ + + if (ch == 'i') { + ch = 'd'; + } + + doubleType = 0; + + /* Each valid conversion will set: + * formatted_buf - the result to be added + * formatted_chars - the length of formatted_buf in characters + * formatted_bytes - the length of formatted_buf in bytes + */ + switch (ch) { + case '\0': + msg = "format string ended in middle of field specifier"; + goto errorMsg; + case 's': { + formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes); + formatted_chars = Jim_Utf8Length(interp, objv[objIndex]); + if (gotPrecision && (precision < formatted_chars)) { + /* Need to build a (null terminated) truncated string */ + formatted_chars = precision; + formatted_bytes = utf8_index(formatted_buf, precision); + } + break; + } + case 'c': { + jim_wide code; + + if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) { + goto error; + } + /* Just store the value in the 'spec' buffer */ + formatted_bytes = utf8_fromunicode(spec, code); + formatted_buf = spec; + formatted_chars = 1; + break; + } + + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + doubleType = 1; + /* fall through */ + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': { + jim_wide w; + double d; + int length; + + /* Fill in the width and precision */ + if (width) { + p += sprintf(p, "%ld", width); + } + if (gotPrecision) { + p += sprintf(p, ".%ld", precision); + } + + /* Now the modifier, and get the actual value here */ + if (doubleType) { + if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) { + goto error; + } + length = MAX_FLOAT_WIDTH; + } + else { + if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) { + goto error; + } + length = JIM_INTEGER_SPACE; + if (useShort) { + *p++ = 'h'; + if (ch == 'd') { + w = (short)w; + } + else { + w = (unsigned short)w; + } + } + else { + *p++ = 'l'; +#ifdef HAVE_LONG_LONG + if (sizeof(long long) == sizeof(jim_wide)) { + *p++ = 'l'; + } +#endif + } + } + + *p++ = (char) ch; + *p = '\0'; + + /* Adjust length for width and precision */ + if (width > length) { + length = width; + } + if (gotPrecision) { + length += precision; + } + + /* Increase the size of the buffer if needed */ + if (num_buffer_size < length + 1) { + num_buffer_size = length + 1; + num_buffer = Jim_Realloc(num_buffer, num_buffer_size); + } + + if (doubleType) { + snprintf(num_buffer, length + 1, spec, d); + } + else { + formatted_bytes = snprintf(num_buffer, length + 1, spec, w); + } + formatted_chars = formatted_bytes = strlen(num_buffer); + formatted_buf = num_buffer; + break; + } + + default: { + /* Just reuse the 'spec' buffer */ + spec[0] = ch; + spec[1] = '\0'; + Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec); + goto error; + } + } + + if (!gotMinus) { + while (formatted_chars < width) { + Jim_AppendString(interp, resultPtr, &pad, 1); + formatted_chars++; + } + } + + Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes); + + while (formatted_chars < width) { + Jim_AppendString(interp, resultPtr, &pad, 1); + formatted_chars++; + } + + objIndex += gotSequential; + } + if (numBytes) { + Jim_AppendString(interp, resultPtr, span, numBytes); + } + + Jim_Free(num_buffer); + return resultPtr; + + errorMsg: + Jim_SetResultString(interp, msg, -1); + error: + Jim_FreeNewObj(interp, resultPtr); + Jim_Free(num_buffer); + return NULL; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-interactive.c b/release/src/router/usbmodeswitch/jim/jim-interactive.c new file mode 100644 index 0000000000..658e8d8ffc --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-interactive.c @@ -0,0 +1,137 @@ +#include +#include +#include "jim.h" +#include "jimautoconf.h" + +#ifdef USE_LINENOISE +#include +#include "linenoise.h" +#else + +#define MAX_LINE_LEN 512 + +static char *linenoise(const char *prompt) +{ + char *line = malloc(MAX_LINE_LEN); + + fputs(prompt, stdout); + fflush(stdout); + + if (fgets(line, MAX_LINE_LEN, stdin) == NULL) { + free(line); + return NULL; + } + return line; +} +#endif + +int Jim_InteractivePrompt(Jim_Interp *interp) +{ + int retcode = JIM_OK; + char *history_file = NULL; +#ifdef USE_LINENOISE + const char *home; + + home = getenv("HOME"); + if (home && isatty(STDIN_FILENO)) { + int history_len = strlen(home) + sizeof("/.jim_history"); + history_file = Jim_Alloc(history_len); + snprintf(history_file, history_len, "%s/.jim_history", home); + linenoiseHistoryLoad(history_file); + } +#endif + + printf("Welcome to Jim version %d.%d" JIM_NL, + JIM_VERSION / 100, JIM_VERSION % 100); + Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1"); + + while (1) { + Jim_Obj *scriptObjPtr; + const char *result; + int reslen; + char prompt[20]; + const char *str; + + if (retcode != 0) { + const char *retcodestr = Jim_ReturnCode(retcode); + + if (*retcodestr == '?') { + snprintf(prompt, sizeof(prompt) - 3, "[%d] ", retcode); + } + else { + snprintf(prompt, sizeof(prompt) - 3, "[%s] ", retcodestr); + } + } + else { + prompt[0] = '\0'; + } + strcat(prompt, ". "); + + scriptObjPtr = Jim_NewStringObj(interp, "", 0); + Jim_IncrRefCount(scriptObjPtr); + while (1) { + char state; + int len; + char *line; + + line = linenoise(prompt); + if (line == NULL) { + if (errno == EINTR) { + continue; + } + Jim_DecrRefCount(interp, scriptObjPtr); + goto out; + } + if (Jim_Length(scriptObjPtr) != 0) { + Jim_AppendString(interp, scriptObjPtr, "\n", 1); + } + Jim_AppendString(interp, scriptObjPtr, line, -1); + free(line); + str = Jim_GetString(scriptObjPtr, &len); + if (len == 0) { + continue; + } + if (Jim_ScriptIsComplete(str, len, &state)) + break; + + snprintf(prompt, sizeof(prompt), "%c> ", state); + } +#ifdef USE_LINENOISE + if (strcmp(str, "h") == 0) { + /* built-in history command */ + int i; + int len; + char **history = linenoiseHistory(&len); + for (i = 0; i < len; i++) { + printf("%4d %s\n", i + 1, history[i]); + } + Jim_DecrRefCount(interp, scriptObjPtr); + continue; + } + + linenoiseHistoryAdd(Jim_String(scriptObjPtr)); + if (history_file) { + linenoiseHistorySave(history_file); + } +#endif + retcode = Jim_EvalObj(interp, scriptObjPtr); + Jim_DecrRefCount(interp, scriptObjPtr); + + + + if (retcode == JIM_EXIT) { + Jim_Free(history_file); + return JIM_EXIT; + } + if (retcode == JIM_ERR) { + Jim_MakeErrorMessage(interp); + } + result = Jim_GetString(Jim_GetResult(interp), &reslen); + if (reslen) { + printf("%s\n", result); + } + } + out: + Jim_Free(history_file); + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-load.c b/release/src/router/usbmodeswitch/jim/jim-load.c new file mode 100644 index 0000000000..4dc6ed2d87 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-load.c @@ -0,0 +1,126 @@ +#include "jim.h" +#include "jimautoconf.h" +#include + +/* ----------------------------------------------------------------------------- + * Dynamic libraries support (WIN32 not supported) + * ---------------------------------------------------------------------------*/ + +#if defined(HAVE_DLOPEN) || defined(HAVE_DLOPEN_COMPAT) + +#ifdef HAVE_DLFCN_H +#include +#endif + +#ifndef RTLD_NOW + #define RTLD_NOW 0 +#endif +#ifndef RTLD_LOCAL + #define RTLD_LOCAL 0 +#endif + +/** + * Note that Jim_LoadLibrary() requires a path to an existing file. + * + * If it is necessary to search JIM_LIBPATH, use Jim_PackageRequire() instead. + */ +int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName) +{ + void *handle = dlopen(pathName, RTLD_NOW | RTLD_LOCAL); + if (handle == NULL) { + Jim_SetResultFormatted(interp, "error loading extension \"%s\": %s", pathName, + dlerror()); + } + else { + /* We use a unique init symbol depending on the extension name. + * This is done for compatibility between static and dynamic extensions. + * For extension readline.so, the init symbol is "Jim_readlineInit" + */ + const char *pt; + const char *pkgname; + int pkgnamelen; + char initsym[40]; + int (*onload) (Jim_Interp *); + + pt = strrchr(pathName, '/'); + if (pt) { + pkgname = pt + 1; + } + else { + pkgname = pathName; + } + pt = strchr(pkgname, '.'); + if (pt) { + pkgnamelen = pt - pkgname; + } + else { + pkgnamelen = strlen(pkgname); + } + snprintf(initsym, sizeof(initsym), "Jim_%.*sInit", pkgnamelen, pkgname); + + if ((onload = dlsym(handle, initsym)) == NULL) { + Jim_SetResultFormatted(interp, + "No %s symbol found in extension %s", initsym, pathName); + } + else if (onload(interp) != JIM_ERR) { + /* Add this handle to the stack of handles to be freed */ + if (!interp->loadHandles) { + interp->loadHandles = Jim_Alloc(sizeof(*interp->loadHandles)); + Jim_InitStack(interp->loadHandles); + } + Jim_StackPush(interp->loadHandles, handle); + + Jim_SetEmptyResult(interp); + + return JIM_OK; + } + } + if (handle) { + dlclose(handle); + } + return JIM_ERR; +} + +static void JimFreeOneLoadHandle(void *handle) +{ + dlclose(handle); +} + +void Jim_FreeLoadHandles(Jim_Interp *interp) +{ + if (interp->loadHandles) { + Jim_FreeStackElements(interp->loadHandles, JimFreeOneLoadHandle); + Jim_Free(interp->loadHandles); + } +} + +#else /* JIM_DYNLIB */ +int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName) +{ + JIM_NOTUSED(interp); + JIM_NOTUSED(pathName); + + Jim_SetResultString(interp, "the Jim binary has no support for [load]", -1); + return JIM_ERR; +} + +void Jim_FreeLoadHandles(Jim_Interp *interp) +{ +} +#endif /* JIM_DYNLIB */ + +/* [load] */ +static int Jim_LoadCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "libaryFile"); + return JIM_ERR; + } + return Jim_LoadLibrary(interp, Jim_String(argv[1])); +} + +int Jim_loadInit(Jim_Interp *interp) +{ + Jim_CreateCommand(interp, "load", Jim_LoadCoreCommand, NULL, NULL); + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-nvp.c b/release/src/router/usbmodeswitch/jim/jim-nvp.c new file mode 100644 index 0000000000..be94f15911 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-nvp.c @@ -0,0 +1,338 @@ +#include +#include + +int Jim_GetNvp(Jim_Interp *interp, + Jim_Obj *objPtr, const Jim_Nvp * nvp_table, const Jim_Nvp ** result) +{ + Jim_Nvp *n; + int e; + + e = Jim_Nvp_name2value_obj(interp, nvp_table, objPtr, &n); + if (e == JIM_ERR) { + return e; + } + + /* Success? found? */ + if (n->name) { + /* remove const */ + *result = (Jim_Nvp *) n; + return JIM_OK; + } + else { + return JIM_ERR; + } +} + +Jim_Nvp *Jim_Nvp_name2value_simple(const Jim_Nvp * p, const char *name) +{ + while (p->name) { + if (0 == strcmp(name, p->name)) { + break; + } + p++; + } + return ((Jim_Nvp *) (p)); +} + +Jim_Nvp *Jim_Nvp_name2value_nocase_simple(const Jim_Nvp * p, const char *name) +{ + while (p->name) { + if (0 == strcasecmp(name, p->name)) { + break; + } + p++; + } + return ((Jim_Nvp *) (p)); +} + +int Jim_Nvp_name2value_obj(Jim_Interp *interp, const Jim_Nvp * p, Jim_Obj *o, Jim_Nvp ** result) +{ + return Jim_Nvp_name2value(interp, p, Jim_String(o), result); +} + + +int Jim_Nvp_name2value(Jim_Interp *interp, const Jim_Nvp * _p, const char *name, Jim_Nvp ** result) +{ + const Jim_Nvp *p; + + p = Jim_Nvp_name2value_simple(_p, name); + + /* result */ + if (result) { + *result = (Jim_Nvp *) (p); + } + + /* found? */ + if (p->name) { + return JIM_OK; + } + else { + return JIM_ERR; + } +} + +int +Jim_Nvp_name2value_obj_nocase(Jim_Interp *interp, const Jim_Nvp * p, Jim_Obj *o, Jim_Nvp ** puthere) +{ + return Jim_Nvp_name2value_nocase(interp, p, Jim_String(o), puthere); +} + +int +Jim_Nvp_name2value_nocase(Jim_Interp *interp, const Jim_Nvp * _p, const char *name, + Jim_Nvp ** puthere) +{ + const Jim_Nvp *p; + + p = Jim_Nvp_name2value_nocase_simple(_p, name); + + if (puthere) { + *puthere = (Jim_Nvp *) (p); + } + /* found */ + if (p->name) { + return JIM_OK; + } + else { + return JIM_ERR; + } +} + + +int Jim_Nvp_value2name_obj(Jim_Interp *interp, const Jim_Nvp * p, Jim_Obj *o, Jim_Nvp ** result) +{ + int e;; + jim_wide w; + + e = Jim_GetWide(interp, o, &w); + if (e != JIM_OK) { + return e; + } + + return Jim_Nvp_value2name(interp, p, w, result); +} + +Jim_Nvp *Jim_Nvp_value2name_simple(const Jim_Nvp * p, int value) +{ + while (p->name) { + if (value == p->value) { + break; + } + p++; + } + return ((Jim_Nvp *) (p)); +} + + +int Jim_Nvp_value2name(Jim_Interp *interp, const Jim_Nvp * _p, int value, Jim_Nvp ** result) +{ + const Jim_Nvp *p; + + p = Jim_Nvp_value2name_simple(_p, value); + + if (result) { + *result = (Jim_Nvp *) (p); + } + + if (p->name) { + return JIM_OK; + } + else { + return JIM_ERR; + } +} + + +int Jim_GetOpt_Setup(Jim_GetOptInfo * p, Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + memset(p, 0, sizeof(*p)); + p->interp = interp; + p->argc = argc; + p->argv = argv; + + return JIM_OK; +} + +void Jim_GetOpt_Debug(Jim_GetOptInfo * p) +{ + int x; + + fprintf(stderr, "---args---\n"); + for (x = 0; x < p->argc; x++) { + fprintf(stderr, "%2d) %s\n", x, Jim_String(p->argv[x])); + } + fprintf(stderr, "-------\n"); +} + + +int Jim_GetOpt_Obj(Jim_GetOptInfo * goi, Jim_Obj **puthere) +{ + Jim_Obj *o; + + o = NULL; // failure + if (goi->argc) { + // success + o = goi->argv[0]; + goi->argc -= 1; + goi->argv += 1; + } + if (puthere) { + *puthere = o; + } + if (o != NULL) { + return JIM_OK; + } + else { + return JIM_ERR; + } +} + +int Jim_GetOpt_String(Jim_GetOptInfo * goi, char **puthere, int *len) +{ + int r; + Jim_Obj *o; + const char *cp; + + + r = Jim_GetOpt_Obj(goi, &o); + if (r == JIM_OK) { + cp = Jim_GetString(o, len); + if (puthere) { + /* remove const */ + *puthere = (char *)(cp); + } + } + return r; +} + +int Jim_GetOpt_Double(Jim_GetOptInfo * goi, double *puthere) +{ + int r; + Jim_Obj *o; + double _safe; + + if (puthere == NULL) { + puthere = &_safe; + } + + r = Jim_GetOpt_Obj(goi, &o); + if (r == JIM_OK) { + r = Jim_GetDouble(goi->interp, o, puthere); + if (r != JIM_OK) { + Jim_SetResultFormatted(goi->interp, "not a number: %#s", o); + } + } + return r; +} + +int Jim_GetOpt_Wide(Jim_GetOptInfo * goi, jim_wide * puthere) +{ + int r; + Jim_Obj *o; + jim_wide _safe; + + if (puthere == NULL) { + puthere = &_safe; + } + + r = Jim_GetOpt_Obj(goi, &o); + if (r == JIM_OK) { + r = Jim_GetWide(goi->interp, o, puthere); + } + return r; +} + +int Jim_GetOpt_Nvp(Jim_GetOptInfo * goi, const Jim_Nvp * nvp, Jim_Nvp ** puthere) +{ + Jim_Nvp *_safe; + Jim_Obj *o; + int e; + + if (puthere == NULL) { + puthere = &_safe; + } + + e = Jim_GetOpt_Obj(goi, &o); + if (e == JIM_OK) { + e = Jim_Nvp_name2value_obj(goi->interp, nvp, o, puthere); + } + + return e; +} + +void Jim_GetOpt_NvpUnknown(Jim_GetOptInfo * goi, const Jim_Nvp * nvptable, int hadprefix) +{ + if (hadprefix) { + Jim_SetResult_NvpUnknown(goi->interp, goi->argv[-2], goi->argv[-1], nvptable); + } + else { + Jim_SetResult_NvpUnknown(goi->interp, NULL, goi->argv[-1], nvptable); + } +} + + +int Jim_GetOpt_Enum(Jim_GetOptInfo * goi, const char *const *lookup, int *puthere) +{ + int _safe; + Jim_Obj *o; + int e; + + if (puthere == NULL) { + puthere = &_safe; + } + e = Jim_GetOpt_Obj(goi, &o); + if (e == JIM_OK) { + e = Jim_GetEnum(goi->interp, o, lookup, puthere, "option", JIM_ERRMSG); + } + return e; +} + +void +Jim_SetResult_NvpUnknown(Jim_Interp *interp, + Jim_Obj *param_name, Jim_Obj *param_value, const Jim_Nvp * nvp) +{ + if (param_name) { + Jim_SetResultFormatted(interp, "%#s: Unknown: %#s, try one of: ", param_name, param_value); + } + else { + Jim_SetResultFormatted(interp, "Unknown param: %#s, try one of: ", param_value); + } + while (nvp->name) { + const char *a; + const char *b; + + if ((nvp + 1)->name) { + a = nvp->name; + b = ", "; + } + else { + a = "or "; + b = nvp->name; + } + Jim_AppendStrings(interp, Jim_GetResult(interp), a, b, NULL); + nvp++; + } +} + +const char *Jim_Debug_ArgvString(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + static Jim_Obj *debug_string_obj; + + int x; + + if (debug_string_obj) { + Jim_FreeObj(interp, debug_string_obj); + } + + debug_string_obj = Jim_NewEmptyStringObj(interp); + for (x = 0; x < argc; x++) { + Jim_AppendStrings(interp, debug_string_obj, Jim_String(argv[x]), " ", NULL); + } + + return Jim_String(debug_string_obj); +} + +int Jim_nvpInit(Jim_Interp *interp) +{ + /* This is really a helper library, not an extension, but this is the easy way */ + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-nvp.h b/release/src/router/usbmodeswitch/jim/jim-nvp.h new file mode 100644 index 0000000000..12ff889ac8 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-nvp.h @@ -0,0 +1,275 @@ +#ifndef JIM_NVP_H +#define JIM_NVP_H + +#include + +/** Name Value Pairs, aka: NVP + * - Given a string - return the associated int. + * - Given a number - return the associated string. + * . + * + * Very useful when the number is not a simple index into an array of + * known string, or there may be multiple strings (aliases) that mean then same + * thing. + * + * An NVP Table is terminated with ".name = NULL". + * + * During the 'name2value' operation, if no matching string is found + * the pointer to the terminal element (with p->name == NULL) is returned. + * + * Example: + * \code + * const Jim_Nvp yn[] = { + * { "yes", 1 }, + * { "no" , 0 }, + * { "yep", 1 }, + * { "nope", 0 }, + * { NULL, -1 }, + * }; + * + * Jim_Nvp *result + * e = Jim_Nvp_name2value(interp, yn, "y", &result); + * returns &yn[0]; + * e = Jim_Nvp_name2value(interp, yn, "n", &result); + * returns &yn[1]; + * e = Jim_Nvp_name2value(interp, yn, "Blah", &result); + * returns &yn[4]; + * \endcode + * + * During the number2name operation, the first matching value is returned. + */ +typedef struct { + const char *name; + int value; +} Jim_Nvp; + + +int Jim_GetNvp (Jim_Interp *interp, + Jim_Obj *objPtr, + const Jim_Nvp *nvp_table, + const Jim_Nvp **result); + +/* Name Value Pairs Operations */ +Jim_Nvp *Jim_Nvp_name2value_simple(const Jim_Nvp *nvp_table, const char *name); +Jim_Nvp *Jim_Nvp_name2value_nocase_simple(const Jim_Nvp *nvp_table, const char *name); +Jim_Nvp *Jim_Nvp_value2name_simple(const Jim_Nvp *nvp_table, int v); + +int Jim_Nvp_name2value(Jim_Interp *interp, const Jim_Nvp *nvp_table, const char *name, Jim_Nvp **result); +int Jim_Nvp_name2value_nocase(Jim_Interp *interp, const Jim_Nvp *nvp_table, const char *name, Jim_Nvp **result); +int Jim_Nvp_value2name(Jim_Interp *interp, const Jim_Nvp *nvp_table, int value, Jim_Nvp **result); + +int Jim_Nvp_name2value_obj(Jim_Interp *interp, const Jim_Nvp *nvp_table, Jim_Obj *name_obj, Jim_Nvp **result); +int Jim_Nvp_name2value_obj_nocase(Jim_Interp *interp, const Jim_Nvp *nvp_table, Jim_Obj *name_obj, Jim_Nvp **result); +int Jim_Nvp_value2name_obj(Jim_Interp *interp, const Jim_Nvp *nvp_table, Jim_Obj *value_obj, Jim_Nvp **result); + +/** prints a nice 'unknown' parameter error message to the 'result' */ +void Jim_SetResult_NvpUnknown(Jim_Interp *interp, + Jim_Obj *param_name, + Jim_Obj *param_value, + const Jim_Nvp *nvp_table); + + +/** Debug: convert argc/argv into a printable string for printf() debug + * + * \param interp - the interpeter + * \param argc - arg count + * \param argv - the objects + * + * \returns string pointer holding the text. + * + * Note, next call to this function will free the old (last) string. + * + * For example might want do this: + * \code + * fp = fopen("some.file.log", "a"); + * fprintf(fp, "PARAMS are: %s\n", Jim_DebugArgvString(interp, argc, argv)); + * fclose(fp); + * \endcode + */ +const char *Jim_Debug_ArgvString(Jim_Interp *interp, int argc, Jim_Obj *const *argv); + + +/** A TCL -ish GetOpt like code. + * + * Some TCL objects have various "configuration" values. + * For example - in Tcl/Tk the "buttons" have many options. + * + * Usefull when dealing with command options. + * that may come in any order... + * + * Does not support "-foo = 123" type options. + * Only supports tcl type options, like "-foo 123" + */ + +typedef struct jim_getopt { + Jim_Interp *interp; + int argc; + Jim_Obj * const * argv; + int isconfigure; /* non-zero if configure */ +} Jim_GetOptInfo; + +/** GetOpt - how to. + * + * Example (short and incomplete): + * \code + * Jim_GetOptInfo goi; + * + * Jim_GetOpt_Setup(&goi, interp, argc, argv); + * + * while (goi.argc) { + * e = Jim_GetOpt_Nvp(&goi, nvp_options, &n); + * if (e != JIM_OK) { + * Jim_GetOpt_NvpUnknown(&goi, nvp_options, 0); + * return e; + * } + * + * switch (n->value) { + * case ALIVE: + * printf("Option ALIVE specified\n"); + * break; + * case FIRST: + * if (goi.argc < 1) { + * .. not enough args error .. + * } + * Jim_GetOpt_String(&goi, &cp, NULL); + * printf("FIRSTNAME: %s\n", cp); + * case AGE: + * Jim_GetOpt_Wide(&goi, &w); + * printf("AGE: %d\n", (int)(w)); + * break; + * case POLITICS: + * e = Jim_GetOpt_Nvp(&goi, nvp_politics, &n); + * if (e != JIM_OK) { + * Jim_GetOpt_NvpUnknown(&goi, nvp_politics, 1); + * return e; + * } + * } + * } + * + * \endcode + * + */ + +/** Setup GETOPT + * + * \param goi - get opt info to be initialized + * \param interp - jim interp + * \param argc - argc count. + * \param argv - argv (will be copied) + * + * \code + * Jim_GetOptInfo goi; + * + * Jim_GetOptSetup(&goi, interp, argc, argv); + * \endcode + */ + +int Jim_GetOpt_Setup(Jim_GetOptInfo *goi, + Jim_Interp *interp, + int argc, + Jim_Obj * const * argv); + + +/** Debug - Dump parameters to stderr + * \param goi - current parameters + */ +void Jim_GetOpt_Debug(Jim_GetOptInfo *goi); + + + +/** Remove argv[0] from the list. + * + * \param goi - get opt info + * \param puthere - where param is put + * + */ +int Jim_GetOpt_Obj(Jim_GetOptInfo *goi, Jim_Obj **puthere); + +/** Remove argv[0] as string. + * + * \param goi - get opt info + * \param puthere - where param is put + * \param len - return its length + */ +int Jim_GetOpt_String(Jim_GetOptInfo *goi, char **puthere, int *len); + +/** Remove argv[0] as double. + * + * \param goi - get opt info + * \param puthere - where param is put. + * + */ +int Jim_GetOpt_Double(Jim_GetOptInfo *goi, double *puthere); + +/** Remove argv[0] as wide. + * + * \param goi - get opt info + * \param puthere - where param is put. + */ +int Jim_GetOpt_Wide(Jim_GetOptInfo *goi, jim_wide *puthere); + +/** Remove argv[0] as NVP. + * + * \param goi - get opt info + * \param lookup - nvp lookup table + * \param puthere - where param is put. + * + */ +int Jim_GetOpt_Nvp(Jim_GetOptInfo *goi, const Jim_Nvp *lookup, Jim_Nvp **puthere); + +/** Create an appropriate error message for an NVP. + * + * \param goi - options info + * \param lookup - the NVP table that was used. + * \param hadprefix - 0 or 1 if the option had a prefix. + * + * This function will set the "interp->result" to a human readable + * error message listing the available options. + * + * This function assumes the previous option argv[-1] is the unknown string. + * + * If this option had some prefix, then pass "hadprefix = 1" else pass "hadprefix = 0" + * + * Example: + * \code + * + * while (goi.argc) { + * // Get the next option + * e = Jim_GetOpt_Nvp(&goi, cmd_options, &n); + * if (e != JIM_OK) { + * // option was not recognized + * // pass 'hadprefix = 0' because there is no prefix + * Jim_GetOpt_NvpUnknown(&goi, cmd_options, 0); + * return e; + * } + * + * switch (n->value) { + * case OPT_SEX: + * // handle: --sex male | female | lots | needmore + * e = Jim_GetOpt_Nvp(&goi, &nvp_sex, &n); + * if (e != JIM_OK) { + * Jim_GetOpt_NvpUnknown(&ogi, nvp_sex, 1); + * return e; + * } + * printf("Code: (%d) is %s\n", n->value, n->name); + * break; + * case ...: + * [snip] + * } + * } + * \endcode + * + */ +void Jim_GetOpt_NvpUnknown(Jim_GetOptInfo *goi, const Jim_Nvp *lookup, int hadprefix); + + +/** Remove argv[0] as Enum + * + * \param goi - get opt info + * \param lookup - lookup table. + * \param puthere - where param is put. + * + */ +int Jim_GetOpt_Enum(Jim_GetOptInfo *goi, const char * const * lookup, int *puthere); + +#endif diff --git a/release/src/router/usbmodeswitch/jim/jim-pack.c b/release/src/router/usbmodeswitch/jim/jim-pack.c new file mode 100644 index 0000000000..88bf2c1668 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-pack.c @@ -0,0 +1,380 @@ +#include +#include + +/* Provides the [pack] and [unpack] commands to pack and unpack + * a binary string to/from arbitrary width integers and strings. + * + * This may be used to implement the [binary] command. + */ + +/** + * Big endian bit test. + * + * Considers 'bitvect' as a big endian bit stream and returns + * bit 'b' as zero or non-zero. + */ +static int JimTestBitBigEndian(const unsigned char *bitvec, int b) +{ + div_t pos = div(b, 8); + return bitvec[pos.quot] & (1 << (7 - pos.rem)); +} + +/** + * Little endian bit test. + * + * Considers 'bitvect' as a little endian bit stream and returns + * bit 'b' as zero or non-zero. + */ +static int JimTestBitLittleEndian(const unsigned char *bitvec, int b) +{ + div_t pos = div(b, 8); + return bitvec[pos.quot] & (1 << pos.rem); +} + +/** + * Sign extends the given value, 'n' of width 'width' bits. + * + * For example, sign extending 0x80 with a width of 8, produces -128 + */ +static jim_wide JimSignExtend(jim_wide n, int width) +{ + if (width == sizeof(jim_wide) * 8) { + /* Can't sign extend the maximum size integer */ + return n; + } + if (n & ((jim_wide)1 << (width - 1))) { + /* Need to extend */ + n -= ((jim_wide)1 << width); + } + + return n; +} + +/** + * Big endian integer extraction. + * + * Considers 'bitvect' as a big endian bit stream. + * Returns an integer of the given width (in bits) + * starting at the given position (in bits). + * + * The pos/width must represent bits inside bitvec, + * and the width be no more than the width of jim_wide. + */ +static jim_wide JimBitIntBigEndian(const unsigned char *bitvec, int pos, int width) +{ + jim_wide result = 0; + int i; + + /* Aligned, byte extraction */ + if (pos % 8 == 0 && width % 8 == 0) { + for (i = 0; i < width; i += 8) { + result = (result << 8) + bitvec[(pos + i) / 8]; + } + return result; + } + + /* Unaligned */ + for (i = 0; i < width; i++) { + if (JimTestBitBigEndian(bitvec, pos + width - i - 1)) { + result |= ((jim_wide)1 << i); + } + } + + return result; +} + +/** + * Little endian integer extraction. + * + * Like JimBitIntBigEndian() but considers 'bitvect' as a little endian bit stream. + */ +static jim_wide JimBitIntLittleEndian(const unsigned char *bitvec, int pos, int width) +{ + jim_wide result = 0; + int i; + + /* Aligned, byte extraction */ + if (pos % 8 == 0 && width % 8 == 0) { + for (i = 0; i < width; i += 8) { + result += (jim_wide)bitvec[(pos + i) / 8] << i; + } + return result; + } + + /* Unaligned */ + for (i = 0; i < width; i++) { + if (JimTestBitLittleEndian(bitvec, pos + i)) { + result |= ((jim_wide)1 << i); + } + } + + return result; +} + +/** + * Big endian bit set. + * + * Considers 'bitvect' as a big endian bit stream and sets + * bit 'b' to 'bit' + */ +static void JimSetBitBigEndian(unsigned char *bitvec, int b, int bit) +{ + div_t pos = div(b, 8); + if (bit) { + bitvec[pos.quot] |= (1 << (7 - pos.rem)); + } + else { + bitvec[pos.quot] &= ~(1 << (7 - pos.rem)); + } +} + +/** + * Little endian bit set. + * + * Considers 'bitvect' as a little endian bit stream and sets + * bit 'b' to 'bit' + */ +static void JimSetBitLittleEndian(unsigned char *bitvec, int b, int bit) +{ + div_t pos = div(b, 8); + if (bit) { + bitvec[pos.quot] |= (1 << pos.rem); + } + else { + bitvec[pos.quot] &= ~(1 << pos.rem); + } +} + +/** + * Big endian integer packing. + * + * Considers 'bitvect' as a big endian bit stream. + * Packs integer 'value' of the given width (in bits) + * starting at the given position (in bits). + * + * The pos/width must represent bits inside bitvec, + * and the width be no more than the width of jim_wide. + */ +static void JimSetBitsIntBigEndian(unsigned char *bitvec, jim_wide value, int pos, int width) +{ + int i; + + /* Common fast option */ + if (pos % 8 == 0 && width == 8) { + bitvec[pos / 8] = value; + return; + } + + for (i = 0; i < width; i++) { + int bit = !!(value & ((jim_wide)1 << i)); + JimSetBitBigEndian(bitvec, pos + width - i - 1, bit); + } +} + +/** + * Little endian version of JimSetBitsIntBigEndian() + */ +static void JimSetBitsIntLittleEndian(unsigned char *bitvec, jim_wide value, int pos, int width) +{ + int i; + + /* Common fast option */ + if (pos % 8 == 0 && width == 8) { + bitvec[pos / 8] = value; + return; + } + + for (i = 0; i < width; i++) { + int bit = !!(value & ((jim_wide)1 << i)); + JimSetBitLittleEndian(bitvec, pos + i, bit); + } +} + +/** + * [unpack] + * + * Usage: unpack binvalue -intbe|-intle|-uintbe|-uintle|-str bitpos bitwidth + * + * Unpacks bits from $binvalue at bit position $bitpos and with $bitwidth. + * Interprets the value according to the type and returns it. + */ +static int Jim_UnpackCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int option; + static const char * const options[] = { "-intbe", "-intle", "-uintbe", "-uintle", "-str", NULL }; + enum { OPT_INTBE, OPT_INTLE, OPT_UINTBE, OPT_UINTLE, OPT_STR, }; + jim_wide pos; + jim_wide width; + + if (argc != 5) { + Jim_WrongNumArgs(interp, 1, argv, "binvalue -intbe|-intle|-uintbe|-uintle|-str bitpos bitwidth"); + return JIM_ERR; + } + if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; + } + + if (Jim_GetWide(interp, argv[3], &pos) != JIM_OK) { + return JIM_ERR; + } + if (Jim_GetWide(interp, argv[4], &width) != JIM_OK) { + return JIM_ERR; + } + + if (option == OPT_STR) { + int len; + const char *str = Jim_GetString(argv[1], &len); + + if (width % 8 || pos % 8) { + Jim_SetResultString(interp, "string field is not on a byte boundary", -1); + return JIM_ERR; + } + + if (pos >= 0 && width > 0 && pos < len * 8) { + if (pos + width > len * 8) { + width = len * 8 - pos; + } + Jim_SetResultString(interp, str + pos / 8, width / 8); + } + return JIM_OK; + } + else { + int len; + const unsigned char *str = (const unsigned char *)Jim_GetString(argv[1], &len); + jim_wide result = 0; + + if (width > sizeof(jim_wide) * 8) { + Jim_SetResultFormatted(interp, "int field is too wide: %#s", argv[4]); + return JIM_ERR; + } + + if (pos >= 0 && width > 0 && pos < len * 8) { + if (pos + width > len * 8) { + width = len * 8 - pos; + } + if (option == OPT_INTBE || option == OPT_UINTBE) { + result = JimBitIntBigEndian(str, pos, width); + } + else { + result = JimBitIntLittleEndian(str, pos, width); + } + if (option == OPT_INTBE || option == OPT_INTLE) { + result = JimSignExtend(result, width); + } + } + Jim_SetResultInt(interp, result); + return JIM_OK; + } +} + +/** + * [pack] + * + * Usage: pack varname value -intle|-intbe|-str width ?bitoffset? + * + * Packs the binary representation of 'value' into the variable of the given name. + * The value is packed according to the given type, width and bitoffset. + * The variable is created if necessary (like [append]) + * Ihe variable is expanded if necessary + */ +static int Jim_PackCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int option; + static const char * const options[] = { "-intle", "-intbe", "-str", NULL }; + enum { OPT_LE, OPT_BE, OPT_STR }; + jim_wide pos = 0; + jim_wide width; + jim_wide value; + Jim_Obj *stringObjPtr; + int len; + int freeobj = 0; + + if (argc != 5 && argc != 6) { + Jim_WrongNumArgs(interp, 1, argv, "varName value -intle|-intbe|-str bitwidth ?bitoffset?"); + return JIM_ERR; + } + if (Jim_GetEnum(interp, argv[3], options, &option, NULL, JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; + } + if (option != OPT_STR && Jim_GetWide(interp, argv[2], &value) != JIM_OK) { + return JIM_ERR; + } + if (Jim_GetWide(interp, argv[4], &width) != JIM_OK) { + return JIM_ERR; + } + if (width <= 0 || (option == OPT_STR && width % 8) || (option != OPT_STR && width > sizeof(jim_wide) * 8)) { + Jim_SetResultFormatted(interp, "bad bitwidth: %#s", argv[5]); + return JIM_ERR; + } + if (argc == 6) { + if (Jim_GetWide(interp, argv[5], &pos) != JIM_OK) { + return JIM_ERR; + } + if (pos < 0 || (option == OPT_STR && pos % 8)) { + Jim_SetResultFormatted(interp, "bad bitoffset: %#s", argv[5]); + return JIM_ERR; + } + } + + stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED); + if (!stringObjPtr) { + /* Create the string if it doesn't exist */ + stringObjPtr = Jim_NewEmptyStringObj(interp); + freeobj = 1; + } + else if (Jim_IsShared(stringObjPtr)) { + freeobj = 1; + stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr); + } + + len = Jim_Length(stringObjPtr) * 8; + + /* Extend the string as necessary first */ + while (len < pos + width) { + Jim_AppendString(interp, stringObjPtr, "", 1); + len += 8; + } + + Jim_SetResultInt(interp, pos + width); + + /* Now set the bits. Note that the the string *must* have no non-string rep + * since we are writing the bytes directly. + */ + Jim_AppendString(interp, stringObjPtr, "", 0); + + if (option == OPT_BE) { + JimSetBitsIntBigEndian((unsigned char *)stringObjPtr->bytes, value, pos, width); + } + else if (option == OPT_LE) { + JimSetBitsIntLittleEndian((unsigned char *)stringObjPtr->bytes, value, pos, width); + } + else { + pos /= 8; + width /= 8; + + if (width > Jim_Length(argv[2])) { + width = Jim_Length(argv[2]); + } + memcpy(stringObjPtr->bytes + pos, Jim_GetString(argv[2], NULL), width); + /* No padding is needed since the string is already extended */ + } + + if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) { + if (freeobj) { + Jim_FreeNewObj(interp, stringObjPtr); + return JIM_ERR; + } + } + return JIM_OK; +} + +int Jim_packInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "pack", "1.0", JIM_ERRMSG)) { + return JIM_ERR; + } + + Jim_CreateCommand(interp, "unpack", Jim_UnpackCmd, NULL, NULL); + Jim_CreateCommand(interp, "pack", Jim_PackCmd, NULL, NULL); + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-package.c b/release/src/router/usbmodeswitch/jim/jim-package.c new file mode 100644 index 0000000000..9caec0d8a3 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-package.c @@ -0,0 +1,259 @@ +#include +#include + +#include "jim.h" +#include "jimautoconf.h" +#include "jim-subcmd.h" + +/* ----------------------------------------------------------------------------- + * Packages handling + * ---------------------------------------------------------------------------*/ + +int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags) +{ + /* If the package was already provided returns an error. */ + Jim_HashEntry *he = Jim_FindHashEntry(&interp->packages, name); + + /* An empty result means the automatic entry. This can be replaced */ + if (he && *(const char *)he->u.val) { + if (flags & JIM_ERRMSG) { + Jim_SetResultFormatted(interp, "package \"%s\" was already provided", name); + } + return JIM_ERR; + } + if (he) { + Jim_DeleteHashEntry(&interp->packages, name); + } + Jim_AddHashEntry(&interp->packages, name, (char *)ver); + return JIM_OK; +} + +static char *JimFindPackage(Jim_Interp *interp, char **prefixes, int prefixc, const char *pkgName) +{ + int i; + char *buf = Jim_Alloc(JIM_PATH_LEN); + + for (i = 0; i < prefixc; i++) { + if (prefixes[i] == NULL) + continue; + + /* Loadable modules are tried first */ +#ifdef jim_ext_load + snprintf(buf, JIM_PATH_LEN, "%s/%s.so", prefixes[i], pkgName); + if (access(buf, R_OK) == 0) { + return buf; + } +#endif + if (strcmp(prefixes[i], ".") == 0) { + snprintf(buf, JIM_PATH_LEN, "%s.tcl", pkgName); + } + else { + snprintf(buf, JIM_PATH_LEN, "%s/%s.tcl", prefixes[i], pkgName); + } + + if (access(buf, R_OK) == 0) { + return buf; + } + } + Jim_Free(buf); + return NULL; +} + +/* Search for a suitable package under every dir specified by JIM_LIBPATH, + * and load it if possible. If a suitable package was loaded with success + * JIM_OK is returned, otherwise JIM_ERR is returned. */ +static int JimLoadPackage(Jim_Interp *interp, const char *name, int flags) +{ + Jim_Obj *libPathObjPtr; + char **prefixes, *path; + int prefixc, i, retCode = JIM_ERR; + + libPathObjPtr = Jim_GetGlobalVariableStr(interp, JIM_LIBPATH, JIM_NONE); + if (libPathObjPtr == NULL) { + prefixc = 0; + libPathObjPtr = NULL; + } + else { + Jim_IncrRefCount(libPathObjPtr); + prefixc = Jim_ListLength(interp, libPathObjPtr); + } + + prefixes = Jim_Alloc(sizeof(char *) * prefixc); + for (i = 0; i < prefixc; i++) { + Jim_Obj *prefixObjPtr; + + if (Jim_ListIndex(interp, libPathObjPtr, i, &prefixObjPtr, JIM_NONE) != JIM_OK) { + prefixes[i] = NULL; + continue; + } + prefixes[i] = Jim_StrDup(Jim_String(prefixObjPtr)); + } + + /* Scan every directory for the the first match */ + path = JimFindPackage(interp, prefixes, prefixc, name); + if (path != NULL) { + char *p = strrchr(path, '.'); + + /* Note: Even if the file fails to load, we consider the package loaded. + * This prevents issues with recursion. + * Use a dummy version of "" to signify this case. + */ + Jim_PackageProvide(interp, name, "", 0); + + /* Try to load/source it */ + if (p && strcmp(p, ".tcl") == 0) { + retCode = Jim_EvalFileGlobal(interp, path); + } +#ifdef jim_ext_load + else { + retCode = Jim_LoadLibrary(interp, path); + } +#endif + if (retCode != JIM_OK) { + /* Upon failure, remove the dummy entry */ + Jim_DeleteHashEntry(&interp->packages, name); + } + Jim_Free(path); + } + for (i = 0; i < prefixc; i++) + Jim_Free(prefixes[i]); + Jim_Free(prefixes); + if (libPathObjPtr) + Jim_DecrRefCount(interp, libPathObjPtr); + return retCode; +} + +int Jim_PackageRequire(Jim_Interp *interp, const char *name, int flags) +{ + Jim_HashEntry *he; + + /* Start with an empty error string */ + Jim_SetResultString(interp, "", 0); + + he = Jim_FindHashEntry(&interp->packages, name); + if (he == NULL) { + /* Try to load the package. */ + int retcode = JimLoadPackage(interp, name, flags); + if (retcode != JIM_OK) { + if (flags & JIM_ERRMSG) { + int len; + + Jim_GetString(Jim_GetResult(interp), &len); + Jim_SetResultFormatted(interp, "%#s%sCan't load package %s", + Jim_GetResult(interp), len ? "\n" : "", name); + } + return retcode; + } + + /* In case the package did no 'package provide' */ + Jim_PackageProvide(interp, name, "1.0", 0); + + /* Now it must exist */ + he = Jim_FindHashEntry(&interp->packages, name); + } + + Jim_SetResultString(interp, he->u.val, -1); + return JIM_OK; +} + +/* + *---------------------------------------------------------------------- + * + * package provide name ?version? + * + * This procedure is invoked to declare that a particular version + * of a particular package is now present in an interpreter. There + * must not be any other version of this package already + * provided in the interpreter. + * + * Results: + * Returns JIM_OK and sets the package version (or 1.0 if not specified). + * + *---------------------------------------------------------------------- + */ +static int package_cmd_provide(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *version = "1.0"; + + if (argc == 2) { + version = Jim_String(argv[1]); + } + return Jim_PackageProvide(interp, Jim_String(argv[0]), version, JIM_ERRMSG); +} + +/* + *---------------------------------------------------------------------- + * + * package require name ?version? + * + * This procedure is load a given package. + * Note that the version is ignored. + * + * Results: + * Returns JIM_OK and sets the package version. + * + *---------------------------------------------------------------------- + */ +static int package_cmd_require(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + /* package require failing is important enough to add to the stack */ + interp->addStackTrace++; + + return Jim_PackageRequire(interp, Jim_String(argv[0]), JIM_ERRMSG); +} + +/* + *---------------------------------------------------------------------- + * + * package list + * + * Returns a list of known packages + * + * Results: + * Returns JIM_OK and sets a list of known packages. + * + *---------------------------------------------------------------------- + */ +static int package_cmd_list(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_HashTableIterator *htiter; + Jim_HashEntry *he; + Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); + + htiter = Jim_GetHashTableIterator(&interp->packages); + while ((he = Jim_NextHashEntry(htiter)) != NULL) { + Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, he->key, -1)); + } + Jim_FreeHashTableIterator(htiter); + + Jim_SetResult(interp, listObjPtr); + + return JIM_OK; +} + +static const jim_subcmd_type package_command_table[] = { + {.cmd = "provide", + .args = "name ?version?", + .function = package_cmd_provide, + .minargs = 1, + .maxargs = 2, + .description = "Indicates that the current script provides the given package"}, + {.cmd = "require", + .args = "name ?version?", + .function = package_cmd_require, + .minargs = 1, + .maxargs = 2, + .description = "Loads the given package by looking in standard places"}, + {.cmd = "list", + .function = package_cmd_list, + .minargs = 0, + .maxargs = 0, + .description = "Lists all known packages"}, + {0} +}; + +int Jim_packageInit(Jim_Interp *interp) +{ + Jim_CreateCommand(interp, "package", Jim_SubCmdProc, (void *)package_command_table, NULL); + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-posix.c b/release/src/router/usbmodeswitch/jim/jim-posix.c new file mode 100644 index 0000000000..0cf36049e2 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-posix.c @@ -0,0 +1,231 @@ + +/* Jim - POSIX extension + * Copyright 2005 Salvatore Sanfilippo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * A copy of the license is also included in the source distribution + * of Jim, as a TXT file name called LICENSE. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "jim.h" +#include "jimautoconf.h" + +#ifdef HAVE_SYS_SYSINFO_H +#include +#endif + +static void Jim_PosixSetError(Jim_Interp *interp) +{ + Jim_SetResultString(interp, strerror(errno), -1); +} + +#if defined(HAVE_FORK) +static int Jim_PosixForkCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + pid_t pid; + + JIM_NOTUSED(argv); + + if (argc != 1) { + Jim_WrongNumArgs(interp, 1, argv, ""); + return JIM_ERR; + } + if ((pid = fork()) == -1) { + Jim_PosixSetError(interp); + return JIM_ERR; + } + Jim_SetResultInt(interp, (jim_wide) pid); + return JIM_OK; +} +#endif + +/* + * os.wait ?-nohang? pid + * + * An interface to waitpid(2) + * + * Returns a 3 element list. + * + * If -nohang is specified, and the process is still alive, returns + * + * {0 none 0} + * + * If the process does not exist or has already been waited for, returns: + * + * {-1 error } + * + * If the process exited normally, returns: + * + * { exit } + * + * If the process terminated on a signal, returns: + * + * { signal } + * + * Otherwise (core dump, stopped, continued, ...), returns: + * + * { other 0} + */ +static int Jim_PosixWaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int nohang = 0; + long pid; + int status; + Jim_Obj *listObj; + const char *type; + int value; + + if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-nohang")) { + nohang = 1; + } + if (argc != nohang + 2) { + Jim_WrongNumArgs(interp, 1, argv, "?-nohang? pid"); + return JIM_ERR; + } + if (Jim_GetLong(interp, argv[nohang + 1], &pid) != JIM_OK) { + return JIM_ERR; + } + + pid = waitpid(pid, &status, nohang ? WNOHANG : 0); + listObj = Jim_NewListObj(interp, NULL, 0); + Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, pid)); + if (pid < 0) { + type = "error"; + value = errno; + } + else if (pid == 0) { + type = "none"; + value = 0; + } + else if (WIFEXITED(status)) { + type = "exit"; + value = WEXITSTATUS(status); + } + else if (WIFSIGNALED(status)) { + type = "signal"; + value = WTERMSIG(status); + } + else { + type = "other"; + value = 0; + } + + Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, type, -1)); + if (pid < 0) { + Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, strerror(value), -1)); + } + else { + Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value)); + } + Jim_SetResult(interp, listObj); + return JIM_OK; +} + +static int Jim_PosixGetidsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *objv[8]; + + if (argc != 1) { + Jim_WrongNumArgs(interp, 1, argv, ""); + return JIM_ERR; + } + objv[0] = Jim_NewStringObj(interp, "uid", -1); + objv[1] = Jim_NewIntObj(interp, getuid()); + objv[2] = Jim_NewStringObj(interp, "euid", -1); + objv[3] = Jim_NewIntObj(interp, geteuid()); + objv[4] = Jim_NewStringObj(interp, "gid", -1); + objv[5] = Jim_NewIntObj(interp, getgid()); + objv[6] = Jim_NewStringObj(interp, "egid", -1); + objv[7] = Jim_NewIntObj(interp, getegid()); + Jim_SetResult(interp, Jim_NewListObj(interp, objv, 8)); + return JIM_OK; +} + +#define JIM_HOST_NAME_MAX 1024 +static int Jim_PosixGethostnameCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + char *buf; + int rc = JIM_OK; + + if (argc != 1) { + Jim_WrongNumArgs(interp, 1, argv, ""); + return JIM_ERR; + } + buf = Jim_Alloc(JIM_HOST_NAME_MAX); + if (gethostname(buf, JIM_HOST_NAME_MAX) == -1) { + Jim_PosixSetError(interp); + rc = JIM_ERR; + } + else { + Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, -1)); + } + return rc; +} + +static int Jim_PosixUptimeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ +#ifdef HAVE_STRUCT_SYSINFO_UPTIME + struct sysinfo info; + + if (argc != 1) { + Jim_WrongNumArgs(interp, 1, argv, ""); + return JIM_ERR; + } + + if (sysinfo(&info) == -1) { + Jim_PosixSetError(interp); + return JIM_ERR; + } + + Jim_SetResultInt(interp, info.uptime); +#else + Jim_SetResultInt(interp, (long)time(NULL)); +#endif + return JIM_OK; +} + +static int Jim_PosixPidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc != 1) { + Jim_WrongNumArgs(interp, 1, argv, ""); + return JIM_ERR; + } + + Jim_SetResultInt(interp, getpid()); + return JIM_OK; +} + +int Jim_posixInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "posix", "1.0", JIM_ERRMSG)) + return JIM_ERR; + +#ifdef HAVE_FORK + Jim_CreateCommand(interp, "os.fork", Jim_PosixForkCommand, NULL, NULL); +#endif + Jim_CreateCommand(interp, "os.wait", Jim_PosixWaitCommand, NULL, NULL); + Jim_CreateCommand(interp, "os.getids", Jim_PosixGetidsCommand, NULL, NULL); + Jim_CreateCommand(interp, "os.gethostname", Jim_PosixGethostnameCommand, NULL, NULL); + Jim_CreateCommand(interp, "os.uptime", Jim_PosixUptimeCommand, NULL, NULL); + Jim_CreateCommand(interp, "pid", Jim_PosixPidCommand, NULL, NULL); + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-readdir.c b/release/src/router/usbmodeswitch/jim/jim-readdir.c new file mode 100644 index 0000000000..67fc956e4f --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-readdir.c @@ -0,0 +1,119 @@ + +/* + * Tcl readdir command. + * + * (c) 2008 Steve Bennett + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + * + * Based on original work by: + *----------------------------------------------------------------------------- + * Copyright 1991-1994 Karl Lehenbauer and Mark Diekhans. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies. Karl Lehenbauer and + * Mark Diekhans make no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + *----------------------------------------------------------------------------- + */ + +#include +#include +#include +#include + +#include "jim.h" +#include "jimautoconf.h" + +/* + *----------------------------------------------------------------------------- + * + * Jim_ReaddirCmd -- + * Implements the rename TCL command: + * readdir ?-nocomplain? dirPath + * + * Results: + * Standard TCL result. + *----------------------------------------------------------------------------- + */ +int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *dirPath; + DIR *dirPtr; + struct dirent *entryPtr; + int nocomplain = 0; + + if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "-nocomplain")) { + nocomplain = 1; + } + if (argc != 2 && !nocomplain) { + Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath"); + return JIM_ERR; + } + + dirPath = Jim_String(argv[1 + nocomplain]); + + dirPtr = opendir(dirPath); + if (dirPtr == NULL) { + if (nocomplain) { + return JIM_OK; + } + Jim_SetResultString(interp, strerror(errno), -1); + return JIM_ERR; + } + Jim_SetResultString(interp, strerror(errno), -1); + + Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0)); + + while ((entryPtr = readdir(dirPtr)) != NULL) { + if (entryPtr->d_name[0] == '.') { + if (entryPtr->d_name[1] == '\0') { + continue; + } + if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0')) + continue; + } + Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, + entryPtr->d_name, -1)); + } + closedir(dirPtr); + + return JIM_OK; +} + +int Jim_readdirInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "readdir", "1.0", JIM_ERRMSG)) + return JIM_ERR; + + Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL); + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-readline.c b/release/src/router/usbmodeswitch/jim/jim-readline.c new file mode 100644 index 0000000000..3990d0c0b3 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-readline.c @@ -0,0 +1,61 @@ + +/* Jim - Readline bindings for Jim + * Copyright 2005 Salvatore Sanfilippo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * A copy of the license is also included in the source distribution + * of Jim, as a TXT file name called LICENSE. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jim.h" +#include "jimautoconf.h" + +#include +#include + +static int JimRlReadlineCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + char *line; + + if (argc != 2) { + Jim_WrongNumArgs(interp, 1, argv, "prompt"); + return JIM_ERR; + } + line = readline(Jim_String(argv[1])); + if (!line) { + return JIM_EXIT; + } + Jim_SetResult(interp, Jim_NewStringObj(interp, line, -1)); + return JIM_OK; +} + +static int JimRlAddHistoryCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc != 2) { + Jim_WrongNumArgs(interp, 1, argv, "string"); + return JIM_ERR; + } + add_history(Jim_String(argv[1])); + return JIM_OK; +} + +int Jim_readlineInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "readline", "1.0", JIM_ERRMSG)) + return JIM_ERR; + + Jim_CreateCommand(interp, "readline.readline", JimRlReadlineCommand, NULL, NULL); + Jim_CreateCommand(interp, "readline.addhistory", JimRlAddHistoryCommand, NULL, NULL); + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-regexp.c b/release/src/router/usbmodeswitch/jim/jim-regexp.c new file mode 100644 index 0000000000..2ccd996cd8 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-regexp.c @@ -0,0 +1,566 @@ +/* + * Implements the regexp and regsub commands for Jim + * + * (c) 2008 Steve Bennett + * + * Uses C library regcomp()/regexec() for the matching. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + * + * Based on code originally from Tcl 6.7: + * + * Copyright 1987-1991 Regents of the University of California + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +#include +#include + +#include "jim.h" +#include "jimautoconf.h" +#include "jimregexp.h" + +static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) +{ + regfree(objPtr->internalRep.regexpValue.compre); + Jim_Free(objPtr->internalRep.regexpValue.compre); +} + +static const Jim_ObjType regexpObjType = { + "regexp", + FreeRegexpInternalRep, + NULL, + NULL, + JIM_TYPE_NONE +}; + +static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags) +{ + regex_t *compre; + const char *pattern; + int ret; + + /* Check if the object is already an uptodate variable */ + if (objPtr->typePtr == ®expObjType && + objPtr->internalRep.regexpValue.compre && objPtr->internalRep.regexpValue.flags == flags) { + /* nothing to do */ + return objPtr->internalRep.regexpValue.compre; + } + + /* Not a regexp or the flags do not match */ + + /* Get the string representation */ + pattern = Jim_String(objPtr); + compre = Jim_Alloc(sizeof(regex_t)); + + if ((ret = regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) { + char buf[100]; + + regerror(ret, compre, buf, sizeof(buf)); + Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf); + regfree(compre); + Jim_Free(compre); + return NULL; + } + + Jim_FreeIntRep(interp, objPtr); + + objPtr->typePtr = ®expObjType; + objPtr->internalRep.regexpValue.flags = flags; + objPtr->internalRep.regexpValue.compre = compre; + + return compre; +} + +int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int opt_indices = 0; + int opt_all = 0; + int opt_inline = 0; + regex_t *regex; + int match, i, j; + int offset = 0; + regmatch_t *pmatch = NULL; + int source_len; + int result = JIM_OK; + const char *pattern; + const char *source_str; + int num_matches = 0; + int num_vars; + Jim_Obj *resultListObj = NULL; + int regcomp_flags = 0; + int eflags = 0; + int option; + enum { + OPT_INDICES, OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END + }; + static const char * const options[] = { + "-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL + }; + + if (argc < 3) { + wrongNumArgs: + Jim_WrongNumArgs(interp, 1, argv, + "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?"); + return JIM_ERR; + } + + for (i = 1; i < argc; i++) { + const char *opt = Jim_String(argv[i]); + + if (*opt != '-') { + break; + } + if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { + return JIM_ERR; + } + if (option == OPT_END) { + i++; + break; + } + switch (option) { + case OPT_INDICES: + opt_indices = 1; + break; + + case OPT_NOCASE: + regcomp_flags |= REG_ICASE; + break; + + case OPT_LINE: + regcomp_flags |= REG_NEWLINE; + break; + + case OPT_ALL: + opt_all = 1; + break; + + case OPT_INLINE: + opt_inline = 1; + break; + + case OPT_START: + if (++i == argc) { + goto wrongNumArgs; + } + if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) { + return JIM_ERR; + } + break; + } + } + if (argc - i < 2) { + goto wrongNumArgs; + } + + regex = SetRegexpFromAny(interp, argv[i], regcomp_flags); + if (!regex) { + return JIM_ERR; + } + + pattern = Jim_String(argv[i]); + source_str = Jim_GetString(argv[i + 1], &source_len); + + num_vars = argc - i - 2; + + if (opt_inline) { + if (num_vars) { + Jim_SetResultString(interp, "regexp match variables not allowed when using -inline", + -1); + result = JIM_ERR; + goto done; + } + num_vars = regex->re_nsub + 1; + } + + pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch)); + + /* If an offset has been specified, adjust for that now. + * If it points past the end of the string, point to the terminating null + */ + if (offset) { + if (offset < 0) { + offset += source_len + 1; + } + if (offset > source_len) { + source_str += source_len; + } + else if (offset > 0) { + source_str += offset; + } + eflags |= REG_NOTBOL; + } + + if (opt_inline) { + resultListObj = Jim_NewListObj(interp, NULL, 0); + } + + next_match: + match = regexec(regex, source_str, num_vars + 1, pmatch, eflags); + if (match >= REG_BADPAT) { + char buf[100]; + + regerror(match, regex, buf, sizeof(buf)); + Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf); + result = JIM_ERR; + goto done; + } + + if (match == REG_NOMATCH) { + goto done; + } + + num_matches++; + + if (opt_all && !opt_inline) { + /* Just count the number of matches, so skip the substitution h */ + goto try_next_match; + } + + /* + * If additional variable names have been specified, return + * index information in those variables. + */ + + j = 0; + for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) { + Jim_Obj *resultObj; + + if (opt_indices) { + resultObj = Jim_NewListObj(interp, NULL, 0); + } + else { + resultObj = Jim_NewStringObj(interp, "", 0); + } + + if (pmatch[j].rm_so == -1) { + if (opt_indices) { + Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1)); + Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1)); + } + } + else { + int len = pmatch[j].rm_eo - pmatch[j].rm_so; + + if (opt_indices) { + Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, + offset + pmatch[j].rm_so)); + Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, + offset + pmatch[j].rm_so + len - 1)); + } + else { + Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, len); + } + } + + if (opt_inline) { + Jim_ListAppendElement(interp, resultListObj, resultObj); + } + else { + /* And now set the result variable */ + result = Jim_SetVariable(interp, argv[i], resultObj); + + if (result != JIM_OK) { + Jim_FreeObj(interp, resultObj); + break; + } + } + } + + try_next_match: + if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) { + if (pmatch[0].rm_eo) { + offset += pmatch[0].rm_eo; + source_str += pmatch[0].rm_eo; + } + else { + source_str++; + offset++; + } + if (*source_str) { + eflags = REG_NOTBOL; + goto next_match; + } + } + + done: + if (result == JIM_OK) { + if (opt_inline) { + Jim_SetResult(interp, resultListObj); + } + else { + Jim_SetResultInt(interp, num_matches); + } + } + + Jim_Free(pmatch); + return result; +} + +#define MAX_SUB_MATCHES 50 + +int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int regcomp_flags = 0; + int regexec_flags = 0; + int opt_all = 0; + int offset = 0; + regex_t *regex; + const char *p; + int result; + regmatch_t pmatch[MAX_SUB_MATCHES + 1]; + int num_matches = 0; + + int i, j, n; + Jim_Obj *varname; + Jim_Obj *resultObj; + const char *source_str; + int source_len; + const char *replace_str; + int replace_len; + const char *pattern; + int option; + enum { + OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_END + }; + static const char * const options[] = { + "-nocase", "-line", "-all", "-start", "--", NULL + }; + + if (argc < 4) { + wrongNumArgs: + Jim_WrongNumArgs(interp, 1, argv, + "?switches? exp string subSpec ?varName?"); + return JIM_ERR; + } + + for (i = 1; i < argc; i++) { + const char *opt = Jim_String(argv[i]); + + if (*opt != '-') { + break; + } + if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { + return JIM_ERR; + } + if (option == OPT_END) { + i++; + break; + } + switch (option) { + case OPT_NOCASE: + regcomp_flags |= REG_ICASE; + break; + + case OPT_LINE: + regcomp_flags |= REG_NEWLINE; + break; + + case OPT_ALL: + opt_all = 1; + break; + + case OPT_START: + if (++i == argc) { + goto wrongNumArgs; + } + if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) { + return JIM_ERR; + } + break; + } + } + if (argc - i != 3 && argc - i != 4) { + goto wrongNumArgs; + } + + regex = SetRegexpFromAny(interp, argv[i], regcomp_flags); + if (!regex) { + return JIM_ERR; + } + pattern = Jim_String(argv[i]); + + source_str = Jim_GetString(argv[i + 1], &source_len); + replace_str = Jim_GetString(argv[i + 2], &replace_len); + varname = argv[i + 3]; + + /* Create the result string */ + resultObj = Jim_NewStringObj(interp, "", 0); + + /* If an offset has been specified, adjust for that now. + * If it points past the end of the string, point to the terminating null + */ + if (offset) { + if (offset < 0) { + offset += source_len + 1; + } + if (offset > source_len) { + offset = source_len; + } + else if (offset < 0) { + offset = 0; + } + } + + /* Copy the part before -start */ + Jim_AppendString(interp, resultObj, source_str, offset); + + /* + * The following loop is to handle multiple matches within the + * same source string; each iteration handles one match and its + * corresponding substitution. If "-all" hasn't been specified + * then the loop body only gets executed once. + */ + + n = source_len - offset; + p = source_str + offset; + do { + int match = regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags); + + if (match >= REG_BADPAT) { + char buf[100]; + + regerror(match, regex, buf, sizeof(buf)); + Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf); + return JIM_ERR; + } + if (match == REG_NOMATCH) { + break; + } + + num_matches++; + + /* + * Copy the portion of the source string before the match to the + * result variable. + */ + Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so); + + /* + * Append the subSpec (replace_str) argument to the variable, making appropriate + * substitutions. This code is a bit hairy because of the backslash + * conventions and because the code saves up ranges of characters in + * subSpec to reduce the number of calls to Jim_SetVar. + */ + + for (j = 0; j < replace_len; j++) { + int idx; + int c = replace_str[j]; + + if (c == '&') { + idx = 0; + } + else if (c == '\\' && j < replace_len) { + c = replace_str[++j]; + if ((c >= '0') && (c <= '9')) { + idx = c - '0'; + } + else if ((c == '\\') || (c == '&')) { + Jim_AppendString(interp, resultObj, replace_str + j, 1); + continue; + } + else { + Jim_AppendString(interp, resultObj, replace_str + j - 1, 2); + continue; + } + } + else { + Jim_AppendString(interp, resultObj, replace_str + j, 1); + continue; + } + if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) { + Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so, + pmatch[idx].rm_eo - pmatch[idx].rm_so); + } + } + + p += pmatch[0].rm_eo; + n -= pmatch[0].rm_eo; + + /* If -all is not specified, or there is no source left, we are done */ + if (!opt_all || n == 0) { + break; + } + + /* An anchored pattern without -line must be done */ + if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') { + break; + } + + /* If the pattern is empty, need to step forwards */ + if (pattern[0] == '\0' && n) { + /* Need to copy the char we are moving over */ + Jim_AppendString(interp, resultObj, p, 1); + p++; + n--; + } + + regexec_flags |= REG_NOTBOL; + } while (n); + + /* + * Copy the portion of the string after the last match to the + * result variable. + */ + Jim_AppendString(interp, resultObj, p, -1); + + /* And now set or return the result variable */ + if (argc - i == 4) { + result = Jim_SetVariable(interp, varname, resultObj); + + if (result == JIM_OK) { + Jim_SetResultInt(interp, num_matches); + } + else { + Jim_FreeObj(interp, resultObj); + } + } + else { + Jim_SetResult(interp, resultObj); + result = JIM_OK; + } + + return result; +} + +int Jim_regexpInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "regexp", "1.0", JIM_ERRMSG)) + return JIM_ERR; + + Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL); + Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL); + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-sdl.c b/release/src/router/usbmodeswitch/jim/jim-sdl.c new file mode 100644 index 0000000000..2e700eb21c --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-sdl.c @@ -0,0 +1,234 @@ + +/* Jim - SDL extension + * Copyright 2005 Salvatore Sanfilippo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * A copy of the license is also included in the source distribution + * of Jim, as a TXT file name called LICENSE. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include "jim.h" +#include "jimautoconf.h" + +#define AIO_CMD_LEN 128 + +typedef struct JimSdlSurface +{ + SDL_Surface *screen; +} JimSdlSurface; + +static void JimSdlSetError(Jim_Interp *interp) +{ + Jim_SetResultString(interp, SDL_GetError(), -1); +} + +static void JimSdlDelProc(Jim_Interp *interp, void *privData) +{ + JimSdlSurface *jss = privData; + + JIM_NOTUSED(interp); + + SDL_FreeSurface(jss->screen); + Jim_Free(jss); +} + +/* Calls to commands created via [sdl.surface] are implemented by this + * C command. */ +static int JimSdlHandlerCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + JimSdlSurface *jss = Jim_CmdPrivData(interp); + int option; + static const char * const options[] = { + "free", "flip", "pixel", "rectangle", "box", "line", "aaline", + "circle", "aacircle", "fcircle", NULL + }; + enum + { OPT_FREE, OPT_FLIP, OPT_PIXEL, OPT_RECTANGLE, OPT_BOX, OPT_LINE, + OPT_AALINE, OPT_CIRCLE, OPT_AACIRCLE, OPT_FCIRCLE + }; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "method ?args ...?"); + return JIM_ERR; + } + if (Jim_GetEnum(interp, argv[1], options, &option, "SDL surface method", JIM_ERRMSG) != JIM_OK) + return JIM_ERR; + if (option == OPT_PIXEL) { + /* PIXEL */ + long x, y, red, green, blue, alpha = 255; + + if (argc != 7 && argc != 8) { + Jim_WrongNumArgs(interp, 2, argv, "x y red green blue ?alpha?"); + return JIM_ERR; + } + if (Jim_GetLong(interp, argv[2], &x) != JIM_OK || + Jim_GetLong(interp, argv[3], &y) != JIM_OK || + Jim_GetLong(interp, argv[4], &red) != JIM_OK || + Jim_GetLong(interp, argv[5], &green) != JIM_OK || + Jim_GetLong(interp, argv[6], &blue) != JIM_OK) { + return JIM_ERR; + } + if (argc == 8 && Jim_GetLong(interp, argv[7], &alpha) != JIM_OK) + return JIM_ERR; + pixelRGBA(jss->screen, x, y, red, green, blue, alpha); + return JIM_OK; + } + else if (option == OPT_RECTANGLE || option == OPT_BOX || + option == OPT_LINE || option == OPT_AALINE) { + /* RECTANGLE, BOX, LINE, AALINE */ + long x1, y1, x2, y2, red, green, blue, alpha = 255; + + if (argc != 9 && argc != 10) { + Jim_WrongNumArgs(interp, 2, argv, "x y red green blue ?alpha?"); + return JIM_ERR; + } + if (Jim_GetLong(interp, argv[2], &x1) != JIM_OK || + Jim_GetLong(interp, argv[3], &y1) != JIM_OK || + Jim_GetLong(interp, argv[4], &x2) != JIM_OK || + Jim_GetLong(interp, argv[5], &y2) != JIM_OK || + Jim_GetLong(interp, argv[6], &red) != JIM_OK || + Jim_GetLong(interp, argv[7], &green) != JIM_OK || + Jim_GetLong(interp, argv[8], &blue) != JIM_OK) { + return JIM_ERR; + } + if (argc == 10 && Jim_GetLong(interp, argv[9], &alpha) != JIM_OK) + return JIM_ERR; + switch (option) { + case OPT_RECTANGLE: + rectangleRGBA(jss->screen, x1, y1, x2, y2, red, green, blue, alpha); + break; + case OPT_BOX: + boxRGBA(jss->screen, x1, y1, x2, y2, red, green, blue, alpha); + break; + case OPT_LINE: + lineRGBA(jss->screen, x1, y1, x2, y2, red, green, blue, alpha); + break; + case OPT_AALINE: + aalineRGBA(jss->screen, x1, y1, x2, y2, red, green, blue, alpha); + break; + } + return JIM_OK; + } + else if (option == OPT_CIRCLE || option == OPT_AACIRCLE || option == OPT_FCIRCLE) { + /* CIRCLE, AACIRCLE, FCIRCLE */ + long x, y, radius, red, green, blue, alpha = 255; + + if (argc != 8 && argc != 9) { + Jim_WrongNumArgs(interp, 2, argv, "x y radius red green blue ?alpha?"); + return JIM_ERR; + } + if (Jim_GetLong(interp, argv[2], &x) != JIM_OK || + Jim_GetLong(interp, argv[3], &y) != JIM_OK || + Jim_GetLong(interp, argv[4], &radius) != JIM_OK || + Jim_GetLong(interp, argv[5], &red) != JIM_OK || + Jim_GetLong(interp, argv[6], &green) != JIM_OK || + Jim_GetLong(interp, argv[7], &blue) != JIM_OK) { + return JIM_ERR; + } + if (argc == 9 && Jim_GetLong(interp, argv[8], &alpha) != JIM_OK) + return JIM_ERR; + switch (option) { + case OPT_CIRCLE: + circleRGBA(jss->screen, x, y, radius, red, green, blue, alpha); + break; + case OPT_AACIRCLE: + aacircleRGBA(jss->screen, x, y, radius, red, green, blue, alpha); + break; + case OPT_FCIRCLE: + filledCircleRGBA(jss->screen, x, y, radius, red, green, blue, alpha); + break; + } + return JIM_OK; + } + else if (option == OPT_FREE) { + /* FREE */ + if (argc != 2) { + Jim_WrongNumArgs(interp, 2, argv, ""); + return JIM_ERR; + } + Jim_DeleteCommand(interp, Jim_String(argv[0])); + return JIM_OK; + } + else if (option == OPT_FLIP) { + /* FLIP */ + if (argc != 2) { + Jim_WrongNumArgs(interp, 2, argv, ""); + return JIM_ERR; + } + SDL_Flip(jss->screen); + return JIM_OK; + } + return JIM_OK; +} + +static int JimSdlSurfaceCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + JimSdlSurface *jss; + char buf[AIO_CMD_LEN]; + Jim_Obj *objPtr; + long screenId, xres, yres; + SDL_Surface *screen; + + if (argc != 3) { + Jim_WrongNumArgs(interp, 1, argv, "xres yres"); + return JIM_ERR; + } + if (Jim_GetLong(interp, argv[1], &xres) != JIM_OK || + Jim_GetLong(interp, argv[2], &yres) != JIM_OK) + return JIM_ERR; + + /* Try to create the surface */ + screen = SDL_SetVideoMode(xres, yres, 32, SDL_SWSURFACE | SDL_ANYFORMAT); + if (screen == NULL) { + JimSdlSetError(interp); + return JIM_ERR; + } + /* Get the next file id */ + if (Jim_EvalGlobal(interp, "if {[catch {incr sdl.surfaceId}]} {set sdl.surfaceId 0}") != JIM_OK) + return JIM_ERR; + objPtr = Jim_GetVariableStr(interp, "sdl.surfaceId", JIM_ERRMSG); + if (objPtr == NULL) + return JIM_ERR; + if (Jim_GetLong(interp, objPtr, &screenId) != JIM_OK) + return JIM_ERR; + + /* Create the SDL screen command */ + jss = Jim_Alloc(sizeof(*jss)); + jss->screen = screen; + sprintf(buf, "sdl.surface%ld", screenId); + Jim_CreateCommand(interp, buf, JimSdlHandlerCommand, jss, JimSdlDelProc); + Jim_SetResultString(interp, buf, -1); + return JIM_OK; +} + +int Jim_sdlInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "sdl", "1.0", JIM_ERRMSG)) + return JIM_ERR; + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + JimSdlSetError(interp); + return JIM_ERR; + } + atexit(SDL_Quit); + Jim_CreateCommand(interp, "sdl.screen", JimSdlSurfaceCommand, NULL, NULL); + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-signal.c b/release/src/router/usbmodeswitch/jim/jim-signal.c new file mode 100644 index 0000000000..6a2a2bebb4 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-signal.c @@ -0,0 +1,514 @@ + +/* + * jim-signal.c + * + */ + +#include +#include +#include +#include + +#include "jim.h" +#include "jimautoconf.h" +#include "jim-subcmd.h" +#include "jim-signal.h" + +#define MAX_SIGNALS (sizeof(jim_wide) * 8) + +static jim_wide *sigloc; +static jim_wide sigsblocked; +static struct sigaction *sa_old; +static int signal_handling[MAX_SIGNALS]; + +/* Make sure to do this as a wide, not int */ +#define sig_to_bit(SIG) ((jim_wide)1 << (SIG)) + +static void signal_handler(int sig) +{ + /* We just remember which signals occurred. Jim_Eval() will + * notice this as soon as it can and throw an error + */ + *sigloc |= sig_to_bit(sig); +} + +static void signal_ignorer(int sig) +{ + /* We just remember which signals occurred */ + sigsblocked |= sig_to_bit(sig); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_SignalId -- + * + * Return a textual identifier for a signal number. + * + * Results: + * This procedure returns a machine-readable textual identifier + * that corresponds to sig. The identifier is the same as the + * #define name in signal.h. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +#define CHECK_SIG(NAME) if (sig == NAME) return #NAME + +const char *Jim_SignalId(int sig) +{ + CHECK_SIG(SIGABRT); + CHECK_SIG(SIGALRM); + CHECK_SIG(SIGBUS); + CHECK_SIG(SIGCHLD); + CHECK_SIG(SIGCONT); + CHECK_SIG(SIGFPE); + CHECK_SIG(SIGHUP); + CHECK_SIG(SIGILL); + CHECK_SIG(SIGINT); +#ifdef SIGIO + CHECK_SIG(SIGIO); +#endif + CHECK_SIG(SIGKILL); + CHECK_SIG(SIGPIPE); + CHECK_SIG(SIGPROF); + CHECK_SIG(SIGQUIT); + CHECK_SIG(SIGSEGV); + CHECK_SIG(SIGSTOP); + CHECK_SIG(SIGSYS); + CHECK_SIG(SIGTERM); + CHECK_SIG(SIGTRAP); + CHECK_SIG(SIGTSTP); + CHECK_SIG(SIGTTIN); + CHECK_SIG(SIGTTOU); + CHECK_SIG(SIGURG); + CHECK_SIG(SIGUSR1); + CHECK_SIG(SIGUSR2); + CHECK_SIG(SIGVTALRM); + CHECK_SIG(SIGWINCH); + CHECK_SIG(SIGXCPU); + CHECK_SIG(SIGXFSZ); +#ifdef SIGPWR + CHECK_SIG(SIGPWR); +#endif +#ifdef SIGCLD + CHECK_SIG(SIGCLD); +#endif +#ifdef SIGEMT + CHECK_SIG(SIGEMT); +#endif +#ifdef SIGLOST + CHECK_SIG(SIGLOST); +#endif +#ifdef SIGPOLL + CHECK_SIG(SIGPOLL); +#endif +#ifdef SIGINFO + CHECK_SIG(SIGINFO); +#endif + return "unknown signal"; +} + +const char *Jim_SignalName(int sig) +{ +#ifdef HAVE_SYS_SIGLIST + if (sig >= 0 && sig < NSIG) { + return sys_siglist[sig]; + } +#endif + return Jim_SignalId(sig); +} + +/** + * Given the name of a signal, returns the signal value if found, + * or returns -1 (and sets an error) if not found. + * We accept -SIGINT, SIGINT, INT or any lowercase version or a number, + * either positive or negative. + */ +static int find_signal_by_name(Jim_Interp *interp, const char *name) +{ + int i; + const char *pt = name; + + /* Remove optional - and SIG from the front of the name */ + if (*pt == '-') { + pt++; + } + if (strncasecmp(name, "sig", 3) == 0) { + pt += 3; + } + if (isdigit(UCHAR(pt[0]))) { + i = atoi(pt); + if (i > 0 && i < MAX_SIGNALS) { + return i; + } + } + else { + for (i = 1; i < MAX_SIGNALS; i++) { + /* Jim_SignalId() returns names such as SIGINT, and + * returns "unknown signal id" if unknown, so this will work + */ + if (strcasecmp(Jim_SignalId(i) + 3, pt) == 0) { + return i; + } + } + } + Jim_SetResultString(interp, "unknown signal ", -1); + Jim_AppendString(interp, Jim_GetResult(interp), name, -1); + + return -1; +} + +#define SIGNAL_ACTION_HANDLE 1 +#define SIGNAL_ACTION_IGNORE -1 +#define SIGNAL_ACTION_DEFAULT 0 + +static int do_signal_cmd(Jim_Interp *interp, int action, int argc, Jim_Obj *const *argv) +{ + struct sigaction sa; + int i; + + if (argc == 0) { + Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0)); + for (i = 1; i < MAX_SIGNALS; i++) { + if (signal_handling[i] == action) { + /* Add signal name to the list */ + Jim_ListAppendElement(interp, Jim_GetResult(interp), + Jim_NewStringObj(interp, Jim_SignalId(i), -1)); + } + } + return JIM_OK; + } + + /* Catch all the signals we care about */ + if (action != SIGNAL_ACTION_DEFAULT) { + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + if (action == SIGNAL_ACTION_HANDLE) { + sa.sa_handler = signal_handler; + } + else { + sa.sa_handler = signal_ignorer; + } + } + + /* Iterate through the provided signals */ + for (i = 0; i < argc; i++) { + int sig = find_signal_by_name(interp, Jim_String(argv[i])); + + if (sig < 0) { + return JIM_ERR; + } + if (action != signal_handling[sig]) { + /* Need to change the action for this signal */ + switch (action) { + case SIGNAL_ACTION_HANDLE: + case SIGNAL_ACTION_IGNORE: + if (signal_handling[sig] == SIGNAL_ACTION_DEFAULT) { + if (!sa_old) { + /* Allocate the structure the first time through */ + sa_old = Jim_Alloc(sizeof(*sa_old) * MAX_SIGNALS); + } + sigaction(sig, &sa, &sa_old[sig]); + } + else { + sigaction(sig, &sa, 0); + } + break; + + case SIGNAL_ACTION_DEFAULT: + /* Restore old handler */ + if (sa_old) { + sigaction(sig, &sa_old[sig], 0); + } + } + signal_handling[sig] = action; + } + } + + return JIM_OK; +} + +static int signal_cmd_handle(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return do_signal_cmd(interp, SIGNAL_ACTION_HANDLE, argc, argv); +} + +static int signal_cmd_ignore(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return do_signal_cmd(interp, SIGNAL_ACTION_IGNORE, argc, argv); +} + +static int signal_cmd_default(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return do_signal_cmd(interp, SIGNAL_ACTION_DEFAULT, argc, argv); +} + +static int signal_set_sigmask_result(Jim_Interp *interp, jim_wide sigmask) +{ + int i; + Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); + + for (i = 0; i < MAX_SIGNALS; i++) { + if (sigmask & sig_to_bit(i)) { + Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, Jim_SignalId(i), -1)); + } + } + Jim_SetResult(interp, listObj); + return JIM_OK; +} + +static int signal_cmd_check(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int clear = 0; + jim_wide mask = 0; + jim_wide blocked; + + if (argc > 0 && Jim_CompareStringImmediate(interp, argv[0], "-clear")) { + clear++; + } + if (argc > clear) { + int i; + + /* Signals specified */ + for (i = clear; i < argc; i++) { + int sig = find_signal_by_name(interp, Jim_String(argv[i])); + + if (sig < 0 || sig >= MAX_SIGNALS) { + return -1; + } + mask |= sig_to_bit(sig); + } + } + else { + /* No signals specified, so check/clear all */ + mask = ~mask; + } + + if ((sigsblocked & mask) == 0) { + /* No matching signals, so empty result and nothing to do */ + return JIM_OK; + } + /* Be careful we don't have a race condition where signals are cleared but not returned */ + blocked = sigsblocked & mask; + if (clear) { + sigsblocked &= ~blocked; + } + /* Set the result */ + signal_set_sigmask_result(interp, blocked); + return JIM_OK; +} + +static int signal_cmd_throw(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int sig = SIGINT; + + if (argc == 1) { + if ((sig = find_signal_by_name(interp, Jim_String(argv[0]))) < 0) { + return JIM_ERR; + } + } + + /* If the signal is ignored (blocked) ... */ + if (signal_handling[sig] == SIGNAL_ACTION_IGNORE) { + sigsblocked |= sig_to_bit(sig); + return JIM_OK; + } + + /* Just set the signal */ + interp->sigmask |= sig_to_bit(sig); + + /* Set the canonical name of the signal as the result */ + Jim_SetResultString(interp, Jim_SignalId(sig), -1); + + /* And simply say we caught the signal */ + return JIM_SIGNAL; +} + +/* + *----------------------------------------------------------------------------- + * + * Jim_SignalCmd -- + * Implements the TCL signal command: + * signal handle|ignore|default|throw ?signals ...? + * signal throw signal + * + * Specifies which signals are handled by Tcl code. + * If the one of the given signals is caught, it causes a JIM_SIGNAL + * exception to be thrown which can be caught by catch. + * + * Use 'signal ignore' to ignore the signal(s) + * Use 'signal default' to go back to the default behaviour + * Use 'signal throw signal' to raise the given signal + * + * If no arguments are given, returns the list of signals which are being handled + * + * Results: + * Standard TCL results. + * + *----------------------------------------------------------------------------- + */ +static const jim_subcmd_type signal_command_table[] = { + { .cmd = "handle", + .args = "?signals ...?", + .function = signal_cmd_handle, + .minargs = 0, + .maxargs = -1, + .description = "Lists handled signals, or adds to handled signals" + }, + { .cmd = "ignore", + .args = "?signals ...?", + .function = signal_cmd_ignore, + .minargs = 0, + .maxargs = -1, + .description = "Lists ignored signals, or adds to ignored signals" + }, + { .cmd = "default", + .args = "?signals ...?", + .function = signal_cmd_default, + .minargs = 0, + .maxargs = -1, + .description = "Lists defaulted signals, or adds to defaulted signals" + }, + { .cmd = "check", + .args = "?-clear? ?signals ...?", + .function = signal_cmd_check, + .minargs = 0, + .maxargs = -1, + .description = "Returns ignored signals which have occurred, and optionally clearing them" + }, + { .cmd = "throw", + .args = "?signal?", + .function = signal_cmd_throw, + .minargs = 0, + .maxargs = 1, + .description = "Raises the given signal (default SIGINT)" + }, + { 0 } +}; + +static int Jim_AlarmCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int ret; + + if (argc != 2) { + Jim_WrongNumArgs(interp, 1, argv, "seconds"); + return JIM_ERR; + } + else { +#ifdef HAVE_UALARM + double t; + + ret = Jim_GetDouble(interp, argv[1], &t); + if (ret == JIM_OK) { + if (t < 1) { + ualarm(t * 1e6, 0); + } + else { + alarm(t); + } + } +#else + long t; + + ret = Jim_GetLong(interp, argv[1], &t); + if (ret == JIM_OK) { + alarm(t); + } +#endif + } + + return ret; +} + +static int Jim_SleepCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int ret; + + if (argc != 2) { + Jim_WrongNumArgs(interp, 1, argv, "seconds"); + return JIM_ERR; + } + else { + double t; + + ret = Jim_GetDouble(interp, argv[1], &t); + if (ret == JIM_OK) { +#ifdef HAVE_USLEEP + if (t < 1) { + usleep(t * 1e6); + } + else +#endif + sleep(t); + } + } + + return ret; +} + +static int Jim_KillCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int sig; + long pid; + Jim_Obj *pidObj; + const char *signame; + + if (argc != 2 && argc != 3) { + Jim_WrongNumArgs(interp, 1, argv, "?SIG|-0? pid"); + return JIM_ERR; + } + + if (argc == 2) { + signame = "SIGTERM"; + pidObj = argv[1]; + } + else { + signame = Jim_String(argv[1]); + pidObj = argv[2]; + } + + /* Special 'kill -0 pid' to determine if a pid exists */ + if (strcmp(signame, "-0") == 0 || strcmp(signame, "0") == 0) { + sig = 0; + } + else { + sig = find_signal_by_name(interp, signame); + if (sig < 0) { + return JIM_ERR; + } + } + + if (Jim_GetLong(interp, pidObj, &pid) != JIM_OK) { + return JIM_ERR; + } + + if (kill(pid, sig) == 0) { + return JIM_OK; + } + + Jim_SetResultString(interp, "kill: Failed to deliver signal", -1); + return JIM_ERR; +} + +int Jim_signalInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "signal", "1.0", JIM_ERRMSG)) + return JIM_ERR; + + /* Teach the jim core how to set a result from a sigmask */ + interp->signal_set_result = signal_set_sigmask_result; + + /* Make sure we know where to store the signals which occur */ + sigloc = &interp->sigmask; + + Jim_CreateCommand(interp, "signal", Jim_SubCmdProc, (void *)signal_command_table, NULL); + Jim_CreateCommand(interp, "alarm", Jim_AlarmCmd, 0, 0); + Jim_CreateCommand(interp, "kill", Jim_KillCmd, 0, 0); + + /* Sleep is slightly dubious here */ + Jim_CreateCommand(interp, "sleep", Jim_SleepCmd, 0, 0); + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-signal.h b/release/src/router/usbmodeswitch/jim/jim-signal.h new file mode 100644 index 0000000000..92e080d8bd --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-signal.h @@ -0,0 +1,24 @@ +#ifndef JIM_SIGNAL_H +#define JIM_SIGNAL_H + +/* + *---------------------------------------------------------------------- + * + * Tcl_SignalId -- + * + * Return a textual identifier for a signal number. + * + * Results: + * This procedure returns a machine-readable textual identifier + * that corresponds to sig. The identifier is the same as the + * #define name in signal.h. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +const char *Jim_SignalId(int sig); +const char *Jim_SignalName(int sig); + +#endif diff --git a/release/src/router/usbmodeswitch/jim/jim-subcmd.c b/release/src/router/usbmodeswitch/jim/jim-subcmd.c new file mode 100644 index 0000000000..2de560ac75 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-subcmd.c @@ -0,0 +1,293 @@ +#include +#include + +#include "jim-subcmd.h" +#include "jimautoconf.h" + +/** + * Implements the common 'commands' subcommand + */ +static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + /* Nothing to do, since the result has already been created */ + return JIM_OK; +} + +/** + * Do-nothing command to support -commands and -usage + */ +static const jim_subcmd_type dummy_subcmd = { + .cmd = "dummy", + .function = subcmd_null, + .flags = JIM_MODFLAG_HIDDEN, +}; + +static void add_commands(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep) +{ + const char *s = ""; + + for (; ct->cmd; ct++) { + if (!(ct->flags & JIM_MODFLAG_HIDDEN)) { + Jim_AppendStrings(interp, Jim_GetResult(interp), s, ct->cmd, NULL); + s = sep; + } + } +} + +static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type, + Jim_Obj *cmd, Jim_Obj *subcmd) +{ + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), ", ", type, + " command \"", Jim_String(subcmd), "\": should be ", NULL); + add_commands(interp, command_table, ", "); +} + +static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc, + Jim_Obj *const *argv) +{ + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_String(argv[0]), + " command ... \", where command is one of: ", NULL); + add_commands(interp, command_table, ", "); +} + +static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd) +{ + if (cmd) { + Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL); + } + Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL); + if (ct->args && *ct->args) { + Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL); + } +} + +static void show_full_usage(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, + Jim_Obj *const *argv) +{ + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + for (; ct->cmd; ct++) { + if (!(ct->flags & JIM_MODFLAG_HIDDEN)) { + /* subcmd */ + add_cmd_usage(interp, ct, argv[0]); + if (ct->description) { + Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n ", ct->description, NULL); + } + Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n", NULL); + } + } +} + +static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_table, Jim_Obj *subcmd) +{ + Jim_SetResultString(interp, "wrong # args: must be \"", -1); + add_cmd_usage(interp, command_table, subcmd); + Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL); +} + +const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table, + int argc, Jim_Obj *const *argv) +{ + const jim_subcmd_type *ct; + const jim_subcmd_type *partial = 0; + int cmdlen; + Jim_Obj *cmd; + const char *cmdstr; + const char *cmdname; + int help = 0; + + cmdname = Jim_String(argv[0]); + + if (argc < 2) { + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), "wrong # args: should be \"", cmdname, + " command ...\"\n", NULL); + Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help\" or \"", + cmdname, " -help command\" for help", NULL); + return 0; + } + + cmd = argv[1]; + + if (argc == 2 && Jim_CompareStringImmediate(interp, cmd, "-usage")) { + /* Show full usage */ + show_full_usage(interp, command_table, argc, argv); + return &dummy_subcmd; + } + + /* Check for the help command */ + if (Jim_CompareStringImmediate(interp, cmd, "-help")) { + if (argc == 2) { + /* Usage for the command, not the subcommand */ + show_cmd_usage(interp, command_table, argc, argv); + return &dummy_subcmd; + } + help = 1; + + /* Skip the 'help' command */ + cmd = argv[2]; + } + + /* Check for special builtin '-commands' command first */ + if (Jim_CompareStringImmediate(interp, cmd, "-commands")) { + /* Build the result here */ + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + add_commands(interp, command_table, " "); + return &dummy_subcmd; + } + + cmdstr = Jim_GetString(cmd, &cmdlen); + + for (ct = command_table; ct->cmd; ct++) { + if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) { + /* Found an exact match */ + break; + } + if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) { + if (partial) { + /* Ambiguous */ + if (help) { + /* Just show the top level help here */ + show_cmd_usage(interp, command_table, argc, argv); + return &dummy_subcmd; + } + bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]); + return 0; + } + partial = ct; + } + continue; + } + + /* If we had an unambiguous partial match */ + if (partial && !ct->cmd) { + ct = partial; + } + + if (!ct->cmd) { + /* No matching command */ + if (help) { + /* Just show the top level help here */ + show_cmd_usage(interp, command_table, argc, argv); + return &dummy_subcmd; + } + bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]); + return 0; + } + + if (help) { + Jim_SetResultString(interp, "Usage: ", -1); + /* subcmd */ + add_cmd_usage(interp, ct, argv[0]); + if (ct->description) { + Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n", ct->description, NULL); + } + return &dummy_subcmd; + } + + /* Check the number of args */ + if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) { + Jim_SetResultString(interp, "wrong # args: must be \"", -1); + /* subcmd */ + add_cmd_usage(interp, ct, argv[0]); + Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL); + + return 0; + } + + /* Good command */ + return ct; +} + +int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv) +{ + int ret = JIM_ERR; + + if (ct) { + if (ct->flags & JIM_MODFLAG_FULLARGV) { + ret = ct->function(interp, argc, argv); + } + else { + ret = ct->function(interp, argc - 2, argv + 2); + } + if (ret < 0) { + set_wrong_args(interp, ct, argv[0]); + ret = JIM_ERR; + } + } + return ret; +} + +int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const jim_subcmd_type *ct = + Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv); + + return Jim_CallSubCmd(interp, ct, argc, argv); +} + +/* The following two functions are for normal commands */ +int +Jim_CheckCmdUsage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc, + Jim_Obj *const *argv) +{ + /* -usage or -help */ + if (argc == 2) { + if (Jim_CompareStringImmediate(interp, argv[1], "-usage") + || Jim_CompareStringImmediate(interp, argv[1], "-help")) { + Jim_SetResultString(interp, "Usage: ", -1); + add_cmd_usage(interp, command_table, NULL); + if (command_table->description) { + Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n", command_table->description, + NULL); + } + return JIM_OK; + } + } + if (argc >= 2 && command_table->function) { + /* This is actually a sub command table */ + + Jim_Obj *nargv[4]; + int nargc = 0; + const char *subcmd = NULL; + + if (Jim_CompareStringImmediate(interp, argv[1], "-subcommands")) { + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + add_commands(interp, (jim_subcmd_type *) command_table->function, " "); + return JIM_OK; + } + + if (Jim_CompareStringImmediate(interp, argv[1], "-subhelp") + || Jim_CompareStringImmediate(interp, argv[1], "-help")) { + subcmd = "-help"; + } + else if (Jim_CompareStringImmediate(interp, argv[1], "-subusage")) { + subcmd = "-usage"; + } + + if (subcmd) { + nargv[nargc++] = Jim_NewStringObj(interp, "$handle", -1); + nargv[nargc++] = Jim_NewStringObj(interp, subcmd, -1); + if (argc >= 3) { + nargv[nargc++] = argv[2]; + } + Jim_ParseSubCmd(interp, (jim_subcmd_type *) command_table->function, nargc, nargv); + Jim_FreeNewObj(interp, nargv[0]); + Jim_FreeNewObj(interp, nargv[1]); + return 0; + } + } + + /* Check the number of args */ + if (argc - 1 < command_table->minargs || (command_table->maxargs >= 0 + && argc - 1 > command_table->maxargs)) { + set_wrong_args(interp, command_table, NULL); + Jim_AppendStrings(interp, Jim_GetResult(interp), "\nUse \"", Jim_String(argv[0]), + " -help\" for help", NULL); + return JIM_ERR; + } + + /* Not usage, but passed arg checking */ + return -1; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-subcmd.h b/release/src/router/usbmodeswitch/jim/jim-subcmd.h new file mode 100644 index 0000000000..3a672eb898 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-subcmd.h @@ -0,0 +1,92 @@ +/* Provides a common approach to implementing Tcl commands + * which implement subcommands + */ +#ifndef JIM_SUBCMD_H +#define JIM_SUBCMD_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define JIM_MODFLAG_HIDDEN 0x0001 /* Don't show the subcommand in usage or commands */ +#define JIM_MODFLAG_FULLARGV 0x0002 /* Subcmd proc gets called with full argv */ + +/* Custom flags start at 0x0100 */ + +/** + * Returns JIM_OK if OK, JIM_ERR (etc.) on error, break, continue, etc. + * Returns -1 if invalid args. + */ +typedef int tclmod_cmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv); + +typedef struct { + const char *cmd; /* Name of the (sub)command */ + const char *args; /* Textual description of allowed args */ + tclmod_cmd_function *function; /* Function implementing the subcommand */ + short minargs; /* Minimum required arguments */ + short maxargs; /* Maximum allowed arguments or -1 if no limit */ + unsigned flags; /* JIM_MODFLAG_... plus custom flags */ + const char *description; /* Description of the subcommand */ +} jim_subcmd_type; + +/** + * Looks up the appropriate subcommand in the given command table and return + * the command function which implements the subcommand. + * NULL will be returned and an appropriate error will be set if the subcommand or + * arguments are invalid. + * + * Typical usage is: + * { + * const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, command_table, argc, argv); + * + * return Jim_CallSubCmd(interp, ct, argc, argv); + * } + * + */ +const jim_subcmd_type * +Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv); + +/** + * Parses the args against the given command table and executes the subcommand if found + * or sets an appropriate error if the subcommand or arguments is invalid. + * + * Can be used directly with Jim_CreateCommand() where the ClientData is the command table. + * + * e.g. Jim_CreateCommand(interp, "mycmd", Jim_SubCmdProc, command_table, NULL); + */ +int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv); + +/** + * Invokes the given subcmd with the given args as returned + * by Jim_ParseSubCmd() + * + * If ct is NULL, returns JIM_ERR, leaving any message. + * Otherwise invokes ct->function + * + * If ct->function returns -1, sets an error message and returns JIM_ERR. + * Otherwise returns the result of ct->function. + */ +int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv); + +/** + * Standard processing for a command. + * + * This does the '-help' and '-usage' check and the number of args checks. + * for a top level command against a single 'jim_subcmd_type' structure. + * + * Additionally, if command_table->function is set, it should point to a sub command table + * and '-subhelp ?subcmd?', '-subusage' and '-subcommands' are then also recognised. + * + * Returns 0 if user requested usage, -1 on arg error, 1 if OK to process. + */ +int +Jim_CheckCmdUsage(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/release/src/router/usbmodeswitch/jim/jim-syslog.c b/release/src/router/usbmodeswitch/jim/jim-syslog.c new file mode 100644 index 0000000000..4e14910549 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-syslog.c @@ -0,0 +1,190 @@ + +/* Syslog interface for tcl + * Copyright Victor Wagner at + * http://www.ice.ru/~vitus/works/tcl.html#syslog + * + * Slightly modified by Steve Bennett + * Ported to Jim by Steve Bennett + */ +#include +#include + +#include "jim.h" +#include "jimautoconf.h" + +typedef struct +{ + int logOpened; + int facility; + int options; + char ident[32]; +} SyslogInfo; + +#ifndef LOG_AUTHPRIV +# define LOG_AUTHPRIV LOG_AUTH +#endif + +static const char * const facilities[] = { + [LOG_AUTHPRIV] = "authpriv", + [LOG_CRON] = "cron", + [LOG_DAEMON] = "daemon", + [LOG_KERN] = "kernel", + [LOG_LPR] = "lpr", + [LOG_MAIL] = "mail", + [LOG_NEWS] = "news", + [LOG_SYSLOG] = "syslog", + [LOG_USER] = "user", + [LOG_UUCP] = "uucp", + [LOG_LOCAL0] = "local0", + [LOG_LOCAL1] = "local1", + [LOG_LOCAL2] = "local2", + [LOG_LOCAL3] = "local3", + [LOG_LOCAL4] = "local4", + [LOG_LOCAL5] = "local5", + [LOG_LOCAL6] = "local6", + [LOG_LOCAL7] = "local7", +}; + +static const char * const priorities[] = { + [LOG_EMERG] = "emerg", + [LOG_ALERT] = "alert", + [LOG_CRIT] = "crit", + [LOG_ERR] = "error", + [LOG_WARNING] = "warning", + [LOG_NOTICE] = "notice", + [LOG_INFO] = "info", + [LOG_DEBUG] = "debug", +}; + +/** + * Deletes the syslog command. + */ +static void Jim_SyslogCmdDelete(Jim_Interp *interp, void *privData) +{ + SyslogInfo *info = (SyslogInfo *) privData; + + if (info->logOpened) { + closelog(); + } + Jim_Free(info); +} + +/* Syslog_Log - + * implements syslog tcl command. General format: syslog ?options? level text + * options -facility -ident -options + * + * syslog ?-facility cron|daemon|...? ?-ident string? ?-options int? ?debug|info|...? text + */ +int Jim_SyslogCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int priority = LOG_INFO; + int i = 1; + SyslogInfo *info = Jim_CmdPrivData(interp); + + if (argc <= 1) { + wrongargs: + Jim_WrongNumArgs(interp, 1, argv, + "?-facility cron|daemon|...? ?-ident string? ?-options int? ?debug|info|...? message"); + return JIM_ERR; + } + while (i < argc - 1) { + if (Jim_CompareStringImmediate(interp, argv[i], "-facility")) { + int entry = + Jim_FindByName(Jim_String(argv[i + 1]), facilities, + sizeof(facilities) / sizeof(*facilities)); + if (entry < 0) { + Jim_SetResultString(interp, "Unknown facility", -1); + return JIM_ERR; + } + if (info->facility != entry) { + info->facility = entry; + if (info->logOpened) { + closelog(); + info->logOpened = 0; + } + } + } + else if (Jim_CompareStringImmediate(interp, argv[i], "-options")) { + long tmp; + + if (Jim_GetLong(interp, argv[i + 1], &tmp) == JIM_ERR) { + return JIM_ERR; + } + info->options = tmp; + if (info->logOpened) { + closelog(); + info->logOpened = 0; + } + } + else if (Jim_CompareStringImmediate(interp, argv[i], "-ident")) { + strncpy(info->ident, Jim_String(argv[i + 1]), sizeof(info->ident)); + info->ident[sizeof(info->ident) - 1] = 0; + if (info->logOpened) { + closelog(); + info->logOpened = 0; + } + } + else { + break; + } + i += 2; + } + + /* There should be either 0, 1 or 2 args left */ + if (i == argc) { + /* No args, but they have set some options, so OK */ + return JIM_OK; + } + + if (i < argc - 1) { + priority = + Jim_FindByName(Jim_String(argv[i]), priorities, + sizeof(priorities) / sizeof(*priorities)); + if (priority < 0) { + Jim_SetResultString(interp, "Unknown priority", -1); + return JIM_ERR; + } + i++; + } + + if (i != argc - 1) { + goto wrongargs; + } + if (!info->logOpened) { + if (!info->ident[0]) { + Jim_Obj *argv0 = Jim_GetGlobalVariableStr(interp, "argv0", JIM_NONE); + + if (argv0) { + strncpy(info->ident, Jim_String(argv0), sizeof(info->ident)); + } + else { + strcpy(info->ident, "Tcl script"); + } + info->ident[sizeof(info->ident) - 1] = 0; + } + openlog(info->ident, info->options, info->facility); + info->logOpened = 1; + } + syslog(priority, "%s", Jim_String(argv[i])); + + return JIM_OK; +} + +int Jim_syslogInit(Jim_Interp *interp) +{ + SyslogInfo *info; + + if (Jim_PackageProvide(interp, "syslog", "1.0", JIM_ERRMSG)) + return JIM_ERR; + + info = Jim_Alloc(sizeof(*info)); + + info->logOpened = 0; + info->options = 0; + info->facility = LOG_USER; + info->ident[0] = 0; + + Jim_CreateCommand(interp, "syslog", Jim_SyslogCmd, info, Jim_SyslogCmdDelete); + + return JIM_OK; +} diff --git a/release/src/router/usbmodeswitch/jim/jim-win32compat.h b/release/src/router/usbmodeswitch/jim/jim-win32compat.h new file mode 100644 index 0000000000..89e01f5375 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim-win32compat.h @@ -0,0 +1,69 @@ +#ifndef JIM_WIN32COMPAT_H +#define JIM_WIN32COMPAT_H + +/* Compatibility for Windows (mingw and msvc, not cygwin */ + +/* Note that at this point we don't yet have access to jimautoconf.h */ +#if defined(_WIN32) || defined(WIN32) +#ifndef STRICT + #define STRICT +#endif +#define WIN32_LEAN_AND_MEAN +#include + +#define HAVE_DLOPEN +void *dlopen(const char *path, int mode); +int dlclose(void *handle); +void *dlsym(void *handle, const char *symbol); +char *dlerror(void); + +#ifdef _MSC_VER +/* These are msvc vs gcc */ + +#if _MSC_VER >= 1000 + #pragma warning(disable:4146) +#endif + +#define strcasecmp _stricmp + +#define jim_wide _int64 +#ifndef LLONG_MAX + #define LLONG_MAX 9223372036854775807I64 +#endif +#ifndef LLONG_MIN + #define LLONG_MIN (-LLONG_MAX - 1I64) +#endif +#define JIM_WIDE_MIN LLONG_MIN +#define JIM_WIDE_MAX LLONG_MAX +#define JIM_WIDE_MODIFIER "I64d" + +#include + +#define HAVE_GETTIMEOFDAY +struct timeval { + long tv_sec; + long tv_usec; +}; + +int gettimeofday(struct timeval *tv, void *unused); + +#define HAVE_OPENDIR +struct dirent { + char *d_name; +}; + +typedef struct DIR { + long handle; /* -1 for failed rewind */ + struct _finddata_t info; + struct dirent result; /* d_name null iff first time */ + char *name; /* null-terminated char string */ +} DIR; + +DIR *opendir(const char *name); +int closedir(DIR *dir); +struct dirent *readdir(DIR *dir); +#endif /* _MSC_VER */ + +#endif /* WIN32 */ + +#endif diff --git a/release/src/router/usbmodeswitch/jim/jim.c b/release/src/router/usbmodeswitch/jim/jim.c new file mode 100644 index 0000000000..8543de4675 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim.c @@ -0,0 +1,14478 @@ + +/* Jim - A small embeddable Tcl interpreter + * + * Copyright 2005 Salvatore Sanfilippo + * Copyright 2005 Clemens Hintze + * Copyright 2005 patthoyts - Pat Thoyts + * Copyright 2008,2009 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com + * Copyright 2008 Andrew Lunn + * Copyright 2008 Duane Ellis + * Copyright 2008 Uwe Klein + * Copyright 2008 Steve Bennett + * Copyright 2009 Nico Coesel + * Copyright 2009 Zachary T Welch zw@superlucidity.net + * Copyright 2009 David Brownell + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + **/ +#define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "jim.h" +#include "jimautoconf.h" +#include "utf8.h" + +#ifdef HAVE_BACKTRACE +#include +#endif +#ifdef HAVE_CRT_EXTERNS_H +#include +#endif + +/* For INFINITY, even if math functions are not enabled */ +#include + +/* We may decide to switch to using $[...] after all, so leave it as an option */ +/*#define EXPRSUGAR_BRACKET*/ + +/* For the no-autoconf case */ +#ifndef TCL_LIBRARY +#define TCL_LIBRARY "." +#endif +#ifndef TCL_PLATFORM_OS +#define TCL_PLATFORM_OS "unknown" +#endif +#ifndef TCL_PLATFORM_PLATFORM +#define TCL_PLATFORM_PLATFORM "unknown" +#endif +#ifndef TCL_PLATFORM_PATH_SEPARATOR +#define TCL_PLATFORM_PATH_SEPARATOR ":" +#endif + +/*#define DEBUG_SHOW_SCRIPT*/ +/*#define DEBUG_SHOW_SCRIPT_TOKENS*/ +/*#define DEBUG_SHOW_SUBST*/ +/*#define DEBUG_SHOW_EXPR*/ +/*#define DEBUG_SHOW_EXPR_TOKENS*/ +/*#define JIM_DEBUG_GC*/ +#ifdef JIM_MAINTAINER +#define JIM_DEBUG_COMMAND +#define JIM_DEBUG_PANIC +#endif + +const char *jim_tt_name(int type); + +#ifdef JIM_DEBUG_PANIC +static void JimPanicDump(int panic_condition, const char *fmt, ...); +#define JimPanic(X) JimPanicDump X +#else +#define JimPanic(X) +#endif + +/* ----------------------------------------------------------------------------- + * Global variables + * ---------------------------------------------------------------------------*/ + +/* A shared empty string for the objects string representation. + * Jim_InvalidateStringRep knows about it and doesn't try to free it. */ +static char JimEmptyStringRep[] = ""; + +/* ----------------------------------------------------------------------------- + * Required prototypes of not exported functions + * ---------------------------------------------------------------------------*/ +static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf); +static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags); +static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr, + int flags); +static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr); +static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr); +static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype, + const char *prefix, const char *const *tablePtr, const char *name); +static void JimDeleteLocalProcs(Jim_Interp *interp); +static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, Jim_Obj *fileNameObj, int linenr, + int argc, Jim_Obj *const *argv); +static int JimEvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv, + Jim_Obj *fileNameObj, int linenr); +static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr); +static int JimSign(jim_wide w); +static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr); +static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen); +static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len); + + +static const Jim_HashTableType JimVariablesHashTableType; + +/* Fast access to the int (wide) value of an object which is known to be of int type */ +#define JimWideValue(objPtr) (objPtr)->internalRep.wideValue + +#define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none") + +static int utf8_tounicode_case(const char *s, int *uc, int upper) +{ + int l = utf8_tounicode(s, uc); + if (upper) { + *uc = utf8_upper(*uc); + } + return l; +} + +/* These can be used in addition to JIM_CASESENS/JIM_NOCASE */ +#define JIM_CHARSET_SCAN 2 +#define JIM_CHARSET_GLOB 0 + +/** + * pattern points to a string like "[^a-z\ub5]" + * + * The pattern may contain trailing chars, which are ignored. + * + * The pattern is matched against unicode char 'c'. + * + * If (flags & JIM_NOCASE), case is ignored when matching. + * If (flags & JIM_CHARSET_SCAN), the considers ^ and ] special at the start + * of the charset, per scan, rather than glob/string match. + * + * If the unicode char 'c' matches that set, returns a pointer to the ']' character, + * or the null character if the ']' is missing. + * + * Returns NULL on no match. + */ +static const char *JimCharsetMatch(const char *pattern, int c, int flags) +{ + int not = 0; + int pchar; + int match = 0; + int nocase = 0; + + if (flags & JIM_NOCASE) { + nocase++; + c = utf8_upper(c); + } + + if (flags & JIM_CHARSET_SCAN) { + if (*pattern == '^') { + not++; + pattern++; + } + + /* Special case. If the first char is ']', it is part of the set */ + if (*pattern == ']') { + goto first; + } + } + + while (*pattern && *pattern != ']') { + /* Exact match */ + if (pattern[0] == '\\') { +first: + pattern += utf8_tounicode_case(pattern, &pchar, nocase); + } + else { + /* Is this a range? a-z */ + int start; + int end; + + pattern += utf8_tounicode_case(pattern, &start, nocase); + if (pattern[0] == '-' && pattern[1]) { + /* skip '-' */ + pattern += utf8_tounicode(pattern, &pchar); + pattern += utf8_tounicode_case(pattern, &end, nocase); + + /* Handle reversed range too */ + if ((c >= start && c <= end) || (c >= end && c <= start)) { + match = 1; + } + continue; + } + pchar = start; + } + + if (pchar == c) { + match = 1; + } + } + if (not) { + match = !match; + } + + return match ? pattern : NULL; +} + +/* Glob-style pattern matching. */ + +/* Note: string *must* be valid UTF-8 sequences + * slen is a char length, not byte counts. + */ +static int GlobMatch(const char *pattern, const char *string, int nocase) +{ + int c; + int pchar; + while (*pattern) { + switch (pattern[0]) { + case '*': + while (pattern[1] == '*') { + pattern++; + } + pattern++; + if (!pattern[0]) { + return 1; /* match */ + } + while (*string) { + /* Recursive call - Does the remaining pattern match anywhere? */ + if (GlobMatch(pattern, string, nocase)) + return 1; /* match */ + string += utf8_tounicode(string, &c); + } + return 0; /* no match */ + + case '?': + string += utf8_tounicode(string, &c); + break; + + case '[': { + string += utf8_tounicode(string, &c); + pattern = JimCharsetMatch(pattern + 1, c, nocase ? JIM_NOCASE : 0); + if (!pattern) { + return 0; + } + if (!*pattern) { + /* Ran out of pattern (no ']') */ + continue; + } + break; + } + case '\\': + if (pattern[1]) { + pattern++; + } + /* fall through */ + default: + string += utf8_tounicode_case(string, &c, nocase); + utf8_tounicode_case(pattern, &pchar, nocase); + if (pchar != c) { + return 0; + } + break; + } + pattern += utf8_tounicode_case(pattern, &pchar, nocase); + if (!*string) { + while (*pattern == '*') { + pattern++; + } + break; + } + } + if (!*pattern && !*string) { + return 1; + } + return 0; +} + +static int JimStringMatch(Jim_Interp *interp, Jim_Obj *patternObj, const char *string, int nocase) +{ + return GlobMatch(Jim_String(patternObj), string, nocase); +} + +/** + * string comparison works on binary data. + * + * Note that the lengths are byte lengths, not char lengths. + */ +static int JimStringCompare(const char *s1, int l1, const char *s2, int l2) +{ + if (l1 < l2) { + return memcmp(s1, s2, l1) <= 0 ? -1 : 1; + } + else if (l2 < l1) { + return memcmp(s1, s2, l2) >= 0 ? 1 : -1; + } + else { + return JimSign(memcmp(s1, s2, l1)); + } +} + +/** + * No-case version. + * + * If maxchars is -1, compares to end of string. + * Otherwise compares at most 'maxchars' characters. + */ +static int JimStringCompareNoCase(const char *s1, const char *s2, int maxchars) +{ + while (*s1 && *s2 && maxchars) { + int c1, c2; + s1 += utf8_tounicode_case(s1, &c1, 1); + s2 += utf8_tounicode_case(s2, &c2, 1); + if (c1 != c2) { + return JimSign(c1 - c2); + } + maxchars--; + } + if (!maxchars) { + return 0; + } + /* One string or both terminated */ + if (*s1) { + return 1; + } + if (*s2) { + return -1; + } + return 0; +} + +/* Search 's1' inside 's2', starting to search from char 'index' of 's2'. + * The index of the first occurrence of s1 in s2 is returned. + * If s1 is not found inside s2, -1 is returned. */ +static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx) +{ + int i; + int l1bytelen; + + if (!l1 || !l2 || l1 > l2) { + return -1; + } + if (idx < 0) + idx = 0; + s2 += utf8_index(s2, idx); + + l1bytelen = utf8_index(s1, l1); + + for (i = idx; i <= l2 - l1; i++) { + int c; + if (memcmp(s2, s1, l1bytelen) == 0) { + return i; + } + s2 += utf8_tounicode(s2, &c); + } + return -1; +} + +/** + * Note: Lengths and return value are in bytes, not chars. + */ +static int JimStringLast(const char *s1, int l1, const char *s2, int l2) +{ + const char *p; + + if (!l1 || !l2 || l1 > l2) + return -1; + + /* Now search for the needle */ + for (p = s2 + l2 - 1; p != s2 - 1; p--) { + if (*p == *s1 && memcmp(s1, p, l1) == 0) { + return p - s2; + } + } + return -1; +} + +#ifdef JIM_UTF8 +/** + * Note: Lengths and return value are in chars. + */ +static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2) +{ + int n = JimStringLast(s1, utf8_index(s1, l1), s2, utf8_index(s2, l2)); + if (n > 0) { + n = utf8_strlen(s2, n); + } + return n; +} +#endif + +int Jim_WideToString(char *buf, jim_wide wideValue) +{ + const char *fmt = "%" JIM_WIDE_MODIFIER; + + return sprintf(buf, fmt, wideValue); +} + +/** + * After an strtol()/strtod()-like conversion, + * check whether something was converted and that + * the only thing left is white space. + * + * Returns JIM_OK or JIM_ERR. + */ +static int JimCheckConversion(const char *str, const char *endptr) +{ + if (str[0] == '\0' || str == endptr) { + return JIM_ERR; + } + + if (endptr[0] != '\0') { + while (*endptr) { + if (!isspace(UCHAR(*endptr))) { + return JIM_ERR; + } + endptr++; + } + } + return JIM_OK; +} + +int Jim_StringToWide(const char *str, jim_wide * widePtr, int base) +{ + char *endptr; + + *widePtr = strtoull(str, &endptr, base); + + return JimCheckConversion(str, endptr); +} + +int Jim_DoubleToString(char *buf, double doubleValue) +{ + int len; + char *buf0 = buf; + + len = sprintf(buf, "%.12g", doubleValue); + + /* Add a final ".0" if it's a number. But not + * for NaN or InF */ + while (*buf) { + if (*buf == '.' || isalpha(UCHAR(*buf))) { + /* inf -> Inf, nan -> Nan */ + if (*buf == 'i' || *buf == 'n') { + *buf = toupper(UCHAR(*buf)); + } + if (*buf == 'I') { + /* Infinity -> Inf */ + buf[3] = '\0'; + len = buf - buf0 + 3; + } + return len; + } + buf++; + } + + *buf++ = '.'; + *buf++ = '0'; + *buf = '\0'; + + return len + 2; +} + +int Jim_StringToDouble(const char *str, double *doublePtr) +{ + char *endptr; + + /* Callers can check for underflow via ERANGE */ + errno = 0; + + *doublePtr = strtod(str, &endptr); + + return JimCheckConversion(str, endptr); +} + +static jim_wide JimPowWide(jim_wide b, jim_wide e) +{ + jim_wide i, res = 1; + + if ((b == 0 && e != 0) || (e < 0)) + return 0; + for (i = 0; i < e; i++) { + res *= b; + } + return res; +} + +/* ----------------------------------------------------------------------------- + * Special functions + * ---------------------------------------------------------------------------*/ +#ifdef JIM_DEBUG_PANIC +void JimPanicDump(int condition, const char *fmt, ...) +{ + va_list ap; + + if (!condition) { + return; + } + + va_start(ap, fmt); + + fprintf(stderr, JIM_NL "JIM INTERPRETER PANIC: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, JIM_NL JIM_NL); + va_end(ap); + +#ifdef HAVE_BACKTRACE + { + void *array[40]; + int size, i; + char **strings; + + size = backtrace(array, 40); + strings = backtrace_symbols(array, size); + for (i = 0; i < size; i++) + fprintf(stderr, "[backtrace] %s" JIM_NL, strings[i]); + fprintf(stderr, "[backtrace] Include the above lines and the output" JIM_NL); + fprintf(stderr, "[backtrace] of 'nm ' in the bug report." JIM_NL); + } +#endif + + abort(); +} +#endif + +/* ----------------------------------------------------------------------------- + * Memory allocation + * ---------------------------------------------------------------------------*/ + +void *Jim_Alloc(int size) +{ + return malloc(size); +} + +void Jim_Free(void *ptr) +{ + free(ptr); +} + +void *Jim_Realloc(void *ptr, int size) +{ + return realloc(ptr, size); +} + +char *Jim_StrDup(const char *s) +{ + return strdup(s); +} + +char *Jim_StrDupLen(const char *s, int l) +{ + char *copy = Jim_Alloc(l + 1); + + memcpy(copy, s, l + 1); + copy[l] = 0; /* Just to be sure, original could be substring */ + return copy; +} + +/* ----------------------------------------------------------------------------- + * Time related functions + * ---------------------------------------------------------------------------*/ + +/* Returns microseconds of CPU used since start. */ +static jim_wide JimClock(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec; +} + +/* ----------------------------------------------------------------------------- + * Hash Tables + * ---------------------------------------------------------------------------*/ + +/* -------------------------- private prototypes ---------------------------- */ +static int JimExpandHashTableIfNeeded(Jim_HashTable *ht); +static unsigned int JimHashTableNextPower(unsigned int size); +static int JimInsertHashEntry(Jim_HashTable *ht, const void *key); + +/* -------------------------- hash functions -------------------------------- */ + +/* Thomas Wang's 32 bit Mix Function */ +unsigned int Jim_IntHashFunction(unsigned int key) +{ + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} + +/* Generic hash function (we are using to multiply by 9 and add the byte + * as Tcl) */ +unsigned int Jim_GenHashFunction(const unsigned char *buf, int len) +{ + unsigned int h = 0; + + while (len--) + h += (h << 3) + *buf++; + return h; +} + +/* ----------------------------- API implementation ------------------------- */ + +/* reset a hashtable already initialized with ht_init(). + * NOTE: This function should only called by ht_destroy(). */ +static void JimResetHashTable(Jim_HashTable *ht) +{ + ht->table = NULL; + ht->size = 0; + ht->sizemask = 0; + ht->used = 0; + ht->collisions = 0; +} + +/* Initialize the hash table */ +int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr) +{ + JimResetHashTable(ht); + ht->type = type; + ht->privdata = privDataPtr; + return JIM_OK; +} + +/* Resize the table to the minimal size that contains all the elements, + * but with the invariant of a USER/BUCKETS ration near to <= 1 */ +int Jim_ResizeHashTable(Jim_HashTable *ht) +{ + int minimal = ht->used; + + if (minimal < JIM_HT_INITIAL_SIZE) + minimal = JIM_HT_INITIAL_SIZE; + return Jim_ExpandHashTable(ht, minimal); +} + +/* Expand or create the hashtable */ +int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size) +{ + Jim_HashTable n; /* the new hashtable */ + unsigned int realsize = JimHashTableNextPower(size), i; + + /* the size is invalid if it is smaller than the number of + * elements already inside the hashtable */ + if (ht->used >= size) + return JIM_ERR; + + Jim_InitHashTable(&n, ht->type, ht->privdata); + n.size = realsize; + n.sizemask = realsize - 1; + n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *)); + + /* Initialize all the pointers to NULL */ + memset(n.table, 0, realsize * sizeof(Jim_HashEntry *)); + + /* Copy all the elements from the old to the new table: + * note that if the old hash table is empty ht->used is zero, + * so Jim_ExpandHashTable just creates an empty hash table. */ + n.used = ht->used; + for (i = 0; ht->used > 0; i++) { + Jim_HashEntry *he, *nextHe; + + if (ht->table[i] == NULL) + continue; + + /* For each hash entry on this slot... */ + he = ht->table[i]; + while (he) { + unsigned int h; + + nextHe = he->next; + /* Get the new element index */ + h = Jim_HashKey(ht, he->key) & n.sizemask; + he->next = n.table[h]; + n.table[h] = he; + ht->used--; + /* Pass to the next element */ + he = nextHe; + } + } + assert(ht->used == 0); + Jim_Free(ht->table); + + /* Remap the new hashtable in the old */ + *ht = n; + return JIM_OK; +} + +/* Add an element to the target hash table */ +int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val) +{ + int idx; + Jim_HashEntry *entry; + + /* Get the index of the new element, or -1 if + * the element already exists. */ + if ((idx = JimInsertHashEntry(ht, key)) == -1) + return JIM_ERR; + + /* Allocates the memory and stores key */ + entry = Jim_Alloc(sizeof(*entry)); + entry->next = ht->table[idx]; + ht->table[idx] = entry; + + /* Set the hash entry fields. */ + Jim_SetHashKey(ht, entry, key); + Jim_SetHashVal(ht, entry, val); + ht->used++; + return JIM_OK; +} + +/* Add an element, discarding the old if the key already exists */ +int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val) +{ + Jim_HashEntry *entry; + + /* Try to add the element. If the key + * does not exists Jim_AddHashEntry will suceed. */ + if (Jim_AddHashEntry(ht, key, val) == JIM_OK) + return JIM_OK; + /* It already exists, get the entry */ + entry = Jim_FindHashEntry(ht, key); + /* Free the old value and set the new one */ + Jim_FreeEntryVal(ht, entry); + Jim_SetHashVal(ht, entry, val); + return JIM_OK; +} + +/* Search and remove an element */ +int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key) +{ + unsigned int h; + Jim_HashEntry *he, *prevHe; + + if (ht->used == 0) + return JIM_ERR; + h = Jim_HashKey(ht, key) & ht->sizemask; + he = ht->table[h]; + + prevHe = NULL; + while (he) { + if (Jim_CompareHashKeys(ht, key, he->key)) { + /* Unlink the element from the list */ + if (prevHe) + prevHe->next = he->next; + else + ht->table[h] = he->next; + Jim_FreeEntryKey(ht, he); + Jim_FreeEntryVal(ht, he); + Jim_Free(he); + ht->used--; + return JIM_OK; + } + prevHe = he; + he = he->next; + } + return JIM_ERR; /* not found */ +} + +/* Destroy an entire hash table */ +int Jim_FreeHashTable(Jim_HashTable *ht) +{ + unsigned int i; + + /* Free all the elements */ + for (i = 0; ht->used > 0; i++) { + Jim_HashEntry *he, *nextHe; + + if ((he = ht->table[i]) == NULL) + continue; + while (he) { + nextHe = he->next; + Jim_FreeEntryKey(ht, he); + Jim_FreeEntryVal(ht, he); + Jim_Free(he); + ht->used--; + he = nextHe; + } + } + /* Free the table and the allocated cache structure */ + Jim_Free(ht->table); + /* Re-initialize the table */ + JimResetHashTable(ht); + return JIM_OK; /* never fails */ +} + +Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key) +{ + Jim_HashEntry *he; + unsigned int h; + + if (ht->used == 0) + return NULL; + h = Jim_HashKey(ht, key) & ht->sizemask; + he = ht->table[h]; + while (he) { + if (Jim_CompareHashKeys(ht, key, he->key)) + return he; + he = he->next; + } + return NULL; +} + +Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht) +{ + Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter)); + + iter->ht = ht; + iter->index = -1; + iter->entry = NULL; + iter->nextEntry = NULL; + return iter; +} + +Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter) +{ + while (1) { + if (iter->entry == NULL) { + iter->index++; + if (iter->index >= (signed)iter->ht->size) + break; + iter->entry = iter->ht->table[iter->index]; + } + else { + iter->entry = iter->nextEntry; + } + if (iter->entry) { + /* We need to save the 'next' here, the iterator user + * may delete the entry we are returning. */ + iter->nextEntry = iter->entry->next; + return iter->entry; + } + } + return NULL; +} + +/* ------------------------- private functions ------------------------------ */ + +/* Expand the hash table if needed */ +static int JimExpandHashTableIfNeeded(Jim_HashTable *ht) +{ + /* If the hash table is empty expand it to the intial size, + * if the table is "full" dobule its size. */ + if (ht->size == 0) + return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE); + if (ht->size == ht->used) + return Jim_ExpandHashTable(ht, ht->size * 2); + return JIM_OK; +} + +/* Our hash table capability is a power of two */ +static unsigned int JimHashTableNextPower(unsigned int size) +{ + unsigned int i = JIM_HT_INITIAL_SIZE; + + if (size >= 2147483648U) + return 2147483648U; + while (1) { + if (i >= size) + return i; + i *= 2; + } +} + +/* Returns the index of a free slot that can be populated with + * an hash entry for the given 'key'. + * If the key already exists, -1 is returned. */ +static int JimInsertHashEntry(Jim_HashTable *ht, const void *key) +{ + unsigned int h; + Jim_HashEntry *he; + + /* Expand the hashtable if needed */ + if (JimExpandHashTableIfNeeded(ht) == JIM_ERR) + return -1; + /* Compute the key hash value */ + h = Jim_HashKey(ht, key) & ht->sizemask; + /* Search if this slot does not already contain the given key */ + he = ht->table[h]; + while (he) { + if (Jim_CompareHashKeys(ht, key, he->key)) + return -1; + he = he->next; + } + return h; +} + +/* ----------------------- StringCopy Hash Table Type ------------------------*/ + +static unsigned int JimStringCopyHTHashFunction(const void *key) +{ + return Jim_GenHashFunction(key, strlen(key)); +} + +static const void *JimStringCopyHTKeyDup(void *privdata, const void *key) +{ + int len = strlen(key); + char *copy = Jim_Alloc(len + 1); + + JIM_NOTUSED(privdata); + + memcpy(copy, key, len); + copy[len] = '\0'; + return copy; +} + +static void *JimStringKeyValCopyHTValDup(void *privdata, const void *val) +{ + int len = strlen(val); + char *copy = Jim_Alloc(len + 1); + + JIM_NOTUSED(privdata); + + memcpy(copy, val, len); + copy[len] = '\0'; + return copy; +} + +static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2) +{ + JIM_NOTUSED(privdata); + + return strcmp(key1, key2) == 0; +} + +static void JimStringCopyHTKeyDestructor(void *privdata, const void *key) +{ + JIM_NOTUSED(privdata); + + Jim_Free((void *)key); /* ATTENTION: const cast */ +} + +static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val) +{ + JIM_NOTUSED(privdata); + + Jim_Free((void *)val); /* ATTENTION: const cast */ +} + +#if 0 +static Jim_HashTableType JimStringCopyHashTableType = { + JimStringCopyHTHashFunction, /* hash function */ + JimStringCopyHTKeyDup, /* key dup */ + NULL, /* val dup */ + JimStringCopyHTKeyCompare, /* key compare */ + JimStringCopyHTKeyDestructor, /* key destructor */ + NULL /* val destructor */ +}; +#endif + +/* This is like StringCopy but does not auto-duplicate the key. + * It's used for intepreter's shared strings. */ +static const Jim_HashTableType JimSharedStringsHashTableType = { + JimStringCopyHTHashFunction, /* hash function */ + NULL, /* key dup */ + NULL, /* val dup */ + JimStringCopyHTKeyCompare, /* key compare */ + JimStringCopyHTKeyDestructor, /* key destructor */ + NULL /* val destructor */ +}; + +/* This is like StringCopy but also automatically handle dynamic + * allocated C strings as values. */ +static const Jim_HashTableType JimStringKeyValCopyHashTableType = { + JimStringCopyHTHashFunction, /* hash function */ + JimStringCopyHTKeyDup, /* key dup */ + JimStringKeyValCopyHTValDup, /* val dup */ + JimStringCopyHTKeyCompare, /* key compare */ + JimStringCopyHTKeyDestructor, /* key destructor */ + JimStringKeyValCopyHTValDestructor, /* val destructor */ +}; + +typedef struct AssocDataValue +{ + Jim_InterpDeleteProc *delProc; + void *data; +} AssocDataValue; + +static void JimAssocDataHashTableValueDestructor(void *privdata, void *data) +{ + AssocDataValue *assocPtr = (AssocDataValue *) data; + + if (assocPtr->delProc != NULL) + assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data); + Jim_Free(data); +} + +static const Jim_HashTableType JimAssocDataHashTableType = { + JimStringCopyHTHashFunction, /* hash function */ + JimStringCopyHTKeyDup, /* key dup */ + NULL, /* val dup */ + JimStringCopyHTKeyCompare, /* key compare */ + JimStringCopyHTKeyDestructor, /* key destructor */ + JimAssocDataHashTableValueDestructor /* val destructor */ +}; + +/* ----------------------------------------------------------------------------- + * Stack - This is a simple generic stack implementation. It is used for + * example in the 'expr' expression compiler. + * ---------------------------------------------------------------------------*/ +void Jim_InitStack(Jim_Stack *stack) +{ + stack->len = 0; + stack->maxlen = 0; + stack->vector = NULL; +} + +void Jim_FreeStack(Jim_Stack *stack) +{ + Jim_Free(stack->vector); +} + +int Jim_StackLen(Jim_Stack *stack) +{ + return stack->len; +} + +void Jim_StackPush(Jim_Stack *stack, void *element) +{ + int neededLen = stack->len + 1; + + if (neededLen > stack->maxlen) { + stack->maxlen = neededLen < 20 ? 20 : neededLen * 2; + stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen); + } + stack->vector[stack->len] = element; + stack->len++; +} + +void *Jim_StackPop(Jim_Stack *stack) +{ + if (stack->len == 0) + return NULL; + stack->len--; + return stack->vector[stack->len]; +} + +void *Jim_StackPeek(Jim_Stack *stack) +{ + if (stack->len == 0) + return NULL; + return stack->vector[stack->len - 1]; +} + +void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr)) +{ + int i; + + for (i = 0; i < stack->len; i++) + freeFunc(stack->vector[i]); +} + +/* ----------------------------------------------------------------------------- + * Parser + * ---------------------------------------------------------------------------*/ + +/* Token types */ +#define JIM_TT_NONE 0 /* No token returned */ +#define JIM_TT_STR 1 /* simple string */ +#define JIM_TT_ESC 2 /* string that needs escape chars conversion */ +#define JIM_TT_VAR 3 /* var substitution */ +#define JIM_TT_DICTSUGAR 4 /* Syntax sugar for [dict get], $foo(bar) */ +#define JIM_TT_CMD 5 /* command substitution */ +/* Note: Keep these three together for TOKEN_IS_SEP() */ +#define JIM_TT_SEP 6 /* word separator. arg is # of tokens. -ve if {*} */ +#define JIM_TT_EOL 7 /* line separator */ +#define JIM_TT_EOF 8 /* end of script */ + +#define JIM_TT_LINE 9 /* special 'start-of-line' token. arg is # of arguments to the command. -ve if {*} */ +#define JIM_TT_WORD 10 /* special 'start-of-word' token. arg is # of tokens to combine. -ve if {*} */ + +/* Additional token types needed for expressions */ +#define JIM_TT_SUBEXPR_START 11 +#define JIM_TT_SUBEXPR_END 12 +#define JIM_TT_SUBEXPR_COMMA 13 +#define JIM_TT_EXPR_INT 14 +#define JIM_TT_EXPR_DOUBLE 15 + +#define JIM_TT_EXPRSUGAR 16 /* $(expression) */ + +/* Operator token types start here */ +#define JIM_TT_EXPR_OP 20 + +#define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF) + +/* Parser states */ +#define JIM_PS_DEF 0 /* Default state */ +#define JIM_PS_QUOTE 1 /* Inside "" */ +#define JIM_PS_DICTSUGAR 2 /* Tokenising abc(def) into 4 separate tokens */ + +/* Parser context structure. The same context is used both to parse + * Tcl scripts and lists. */ +struct JimParserCtx +{ + const char *p; /* Pointer to the point of the program we are parsing */ + int len; /* Remaining length */ + int linenr; /* Current line number */ + const char *tstart; + const char *tend; /* Returned token is at tstart-tend in 'prg'. */ + int tline; /* Line number of the returned token */ + int tt; /* Token type */ + int eof; /* Non zero if EOF condition is true. */ + int state; /* Parser state */ + int comment; /* Non zero if the next chars may be a comment. */ + char missing; /* At end of parse, ' ' if complete, '{' if braces incomplete, '"' if quotes incomplete */ + int missingline; /* Line number starting the missing token */ +}; + +/** + * Results of missing quotes, braces, etc. from parsing. + */ +struct JimParseResult { + char missing; /* From JimParserCtx.missing */ + int line; /* From JimParserCtx.missingline */ +}; + +static int JimParseScript(struct JimParserCtx *pc); +static int JimParseSep(struct JimParserCtx *pc); +static int JimParseEol(struct JimParserCtx *pc); +static int JimParseCmd(struct JimParserCtx *pc); +static int JimParseQuote(struct JimParserCtx *pc); +static int JimParseVar(struct JimParserCtx *pc); +static int JimParseBrace(struct JimParserCtx *pc); +static int JimParseStr(struct JimParserCtx *pc); +static int JimParseComment(struct JimParserCtx *pc); +static void JimParseSubCmd(struct JimParserCtx *pc); +static int JimParseSubQuote(struct JimParserCtx *pc); +static void JimParseSubCmd(struct JimParserCtx *pc); +static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc); + +/* Initialize a parser context. + * 'prg' is a pointer to the program text, linenr is the line + * number of the first line contained in the program. */ +static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr) +{ + pc->p = prg; + pc->len = len; + pc->tstart = NULL; + pc->tend = NULL; + pc->tline = 0; + pc->tt = JIM_TT_NONE; + pc->eof = 0; + pc->state = JIM_PS_DEF; + pc->linenr = linenr; + pc->comment = 1; + pc->missing = ' '; + pc->missingline = linenr; +} + +static int JimParseScript(struct JimParserCtx *pc) +{ + while (1) { /* the while is used to reiterate with continue if needed */ + if (!pc->len) { + pc->tstart = pc->p; + pc->tend = pc->p - 1; + pc->tline = pc->linenr; + pc->tt = JIM_TT_EOL; + pc->eof = 1; + return JIM_OK; + } + switch (*(pc->p)) { + case '\\': + if (*(pc->p + 1) == '\n' && pc->state == JIM_PS_DEF) { + return JimParseSep(pc); + } + else { + pc->comment = 0; + return JimParseStr(pc); + } + break; + case ' ': + case '\t': + case '\r': + if (pc->state == JIM_PS_DEF) + return JimParseSep(pc); + else { + pc->comment = 0; + return JimParseStr(pc); + } + break; + case '\n': + case ';': + pc->comment = 1; + if (pc->state == JIM_PS_DEF) + return JimParseEol(pc); + else + return JimParseStr(pc); + break; + case '[': + pc->comment = 0; + return JimParseCmd(pc); + break; + case '$': + pc->comment = 0; + if (JimParseVar(pc) == JIM_ERR) { + pc->tstart = pc->tend = pc->p++; + pc->len--; + pc->tline = pc->linenr; + pc->tt = JIM_TT_STR; + return JIM_OK; + } + else + return JIM_OK; + break; + case '#': + if (pc->comment) { + JimParseComment(pc); + continue; + } + else { + return JimParseStr(pc); + } + default: + pc->comment = 0; + return JimParseStr(pc); + break; + } + return JIM_OK; + } +} + +static int JimParseSep(struct JimParserCtx *pc) +{ + pc->tstart = pc->p; + pc->tline = pc->linenr; + while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || + (*pc->p == '\\' && *(pc->p + 1) == '\n')) { + if (*pc->p == '\\') { + pc->p++; + pc->len--; + pc->linenr++; + } + pc->p++; + pc->len--; + } + pc->tend = pc->p - 1; + pc->tt = JIM_TT_SEP; + return JIM_OK; +} + +static int JimParseEol(struct JimParserCtx *pc) +{ + pc->tstart = pc->p; + pc->tline = pc->linenr; + while (*pc->p == ' ' || *pc->p == '\n' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == ';') { + if (*pc->p == '\n') + pc->linenr++; + pc->p++; + pc->len--; + } + pc->tend = pc->p - 1; + pc->tt = JIM_TT_EOL; + return JIM_OK; +} + +/* +** Here are the rules for parsing: +** {braced expression} +** - Count open and closing braces +** - Backslash escapes meaning of braces +** +** "quoted expression" +** - First double quote at start of word terminates the expression +** - Backslash escapes quote and bracket +** - [commands brackets] are counted/nested +** - command rules apply within [brackets], not quoting rules (i.e. quotes have their own rules) +** +** [command expression] +** - Count open and closing brackets +** - Backslash escapes quote, bracket and brace +** - [commands brackets] are counted/nested +** - "quoted expressions" are parsed according to quoting rules +** - {braced expressions} are parsed according to brace rules +** +** For everything, backslash escapes the next char, newline increments current line +*/ + +/** + * Parses a braced expression starting at pc->p. + * + * Positions the parser at the end of the braced expression, + * sets pc->tend and possibly pc->missing. + */ +static void JimParseSubBrace(struct JimParserCtx *pc) +{ + int level = 1; + + /* Skip the brace */ + pc->p++; + pc->len--; + while (pc->len) { + switch (*pc->p) { + case '\\': + if (pc->len > 1) { + if (*++pc->p == '\n') { + pc->linenr++; + } + pc->len--; + } + break; + + case '{': + level++; + break; + + case '}': + if (--level == 0) { + pc->tend = pc->p - 1; + pc->p++; + pc->len--; + return; + } + break; + + case '\n': + pc->linenr++; + break; + } + pc->p++; + pc->len--; + } + pc->missing = '{'; + pc->missingline = pc->tline; + pc->tend = pc->p - 1; +} + +/** + * Parses a quoted expression starting at pc->p. + * + * Positions the parser at the end of the quoted expression, + * sets pc->tend and possibly pc->missing. + * + * Returns the type of the token of the string, + * either JIM_TT_ESC (if it contains values which need to be [subst]ed) + * or JIM_TT_STR. + */ +static int JimParseSubQuote(struct JimParserCtx *pc) +{ + int tt = JIM_TT_STR; + int line = pc->tline; + + /* Skip the quote */ + pc->p++; + pc->len--; + while (pc->len) { + switch (*pc->p) { + case '\\': + if (pc->len > 1) { + if (*++pc->p == '\n') { + pc->linenr++; + } + pc->len--; + tt = JIM_TT_ESC; + } + break; + + case '"': + pc->tend = pc->p - 1; + pc->p++; + pc->len--; + return tt; + + case '[': + JimParseSubCmd(pc); + tt = JIM_TT_ESC; + continue; + + case '\n': + pc->linenr++; + break; + + case '$': + tt = JIM_TT_ESC; + break; + } + pc->p++; + pc->len--; + } + pc->missing = '"'; + pc->missingline = line; + pc->tend = pc->p - 1; + return tt; +} + +/** + * Parses a [command] expression starting at pc->p. + * + * Positions the parser at the end of the command expression, + * sets pc->tend and possibly pc->missing. + */ +static void JimParseSubCmd(struct JimParserCtx *pc) +{ + int level = 1; + int startofword = 1; + int line = pc->tline; + + /* Skip the bracket */ + pc->p++; + pc->len--; + while (pc->len) { + switch (*pc->p) { + case '\\': + if (pc->len > 1) { + if (*++pc->p == '\n') { + pc->linenr++; + } + pc->len--; + } + break; + + case '[': + level++; + break; + + case ']': + if (--level == 0) { + pc->tend = pc->p - 1; + pc->p++; + pc->len--; + return; + } + break; + + case '"': + if (startofword) { + JimParseSubQuote(pc); + continue; + } + break; + + case '{': + JimParseSubBrace(pc); + startofword = 0; + continue; + + case '\n': + pc->linenr++; + break; + } + startofword = isspace(UCHAR(*pc->p)); + pc->p++; + pc->len--; + } + pc->missing = '['; + pc->missingline = line; + pc->tend = pc->p - 1; +} + +static int JimParseBrace(struct JimParserCtx *pc) +{ + pc->tstart = pc->p + 1; + pc->tline = pc->linenr; + pc->tt = JIM_TT_STR; + JimParseSubBrace(pc); + return JIM_OK; +} + +static int JimParseCmd(struct JimParserCtx *pc) +{ + pc->tstart = pc->p + 1; + pc->tline = pc->linenr; + pc->tt = JIM_TT_CMD; + JimParseSubCmd(pc); + return JIM_OK; +} + +static int JimParseQuote(struct JimParserCtx *pc) +{ + pc->tstart = pc->p + 1; + pc->tline = pc->linenr; + pc->tt = JimParseSubQuote(pc); + return JIM_OK; +} + +static int JimParseVar(struct JimParserCtx *pc) +{ + /* skip the $ */ + pc->p++; + pc->len--; + +#ifdef EXPRSUGAR_BRACKET + if (*pc->p == '[') { + /* Parse $[...] expr shorthand syntax */ + JimParseCmd(pc); + pc->tt = JIM_TT_EXPRSUGAR; + return JIM_OK; + } +#endif + + pc->tstart = pc->p; + pc->tt = JIM_TT_VAR; + pc->tline = pc->linenr; + + if (*pc->p == '{') { + pc->tstart = ++pc->p; + pc->len--; + + while (pc->len && *pc->p != '}') { + if (*pc->p == '\n') { + pc->linenr++; + } + pc->p++; + pc->len--; + } + pc->tend = pc->p - 1; + if (pc->len) { + pc->p++; + pc->len--; + } + } + else { + while (1) { + /* Skip double colon, but not single colon! */ + if (pc->p[0] == ':' && pc->p[1] == ':') { + pc->p += 2; + pc->len -= 2; + continue; + } + if (isalnum(UCHAR(*pc->p)) || *pc->p == '_') { + pc->p++; + pc->len--; + continue; + } + break; + } + /* Parse [dict get] syntax sugar. */ + if (*pc->p == '(') { + int count = 1; + const char *paren = NULL; + + pc->tt = JIM_TT_DICTSUGAR; + + while (count && pc->len) { + pc->p++; + pc->len--; + if (*pc->p == '\\' && pc->len >= 1) { + pc->p++; + pc->len--; + } + else if (*pc->p == '(') { + count++; + } + else if (*pc->p == ')') { + paren = pc->p; + count--; + } + } + if (count == 0) { + pc->p++; + pc->len--; + } + else if (paren) { + /* Did not find a matching paren. Back up */ + paren++; + pc->len += (pc->p - paren); + pc->p = paren; + } +#ifndef EXPRSUGAR_BRACKET + if (*pc->tstart == '(') { + pc->tt = JIM_TT_EXPRSUGAR; + } +#endif + } + pc->tend = pc->p - 1; + } + /* Check if we parsed just the '$' character. + * That's not a variable so an error is returned + * to tell the state machine to consider this '$' just + * a string. */ + if (pc->tstart == pc->p) { + pc->p--; + pc->len++; + return JIM_ERR; + } + return JIM_OK; +} + +static int JimParseStr(struct JimParserCtx *pc) +{ + int newword = (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL || + pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR); + if (newword && *pc->p == '{') { + return JimParseBrace(pc); + } + else if (newword && *pc->p == '"') { + pc->state = JIM_PS_QUOTE; + pc->p++; + pc->len--; + /* In case the end quote is missing */ + pc->missingline = pc->tline; + } + pc->tstart = pc->p; + pc->tline = pc->linenr; + while (1) { + if (pc->len == 0) { + if (pc->state == JIM_PS_QUOTE) { + pc->missing = '"'; + } + pc->tend = pc->p - 1; + pc->tt = JIM_TT_ESC; + return JIM_OK; + } + switch (*pc->p) { + case '\\': + if (pc->state == JIM_PS_DEF && *(pc->p + 1) == '\n') { + pc->tend = pc->p - 1; + pc->tt = JIM_TT_ESC; + return JIM_OK; + } + if (pc->len >= 2) { + if (*(pc->p + 1) == '\n') { + pc->linenr++; + } + pc->p++; + pc->len--; + } + break; + case '(': + /* If the following token is not '$' just keep going */ + if (pc->len > 1 && pc->p[1] != '$') { + break; + } + case ')': + /* Only need a separate ')' token if the previous was a var */ + if (*pc->p == '(' || pc->tt == JIM_TT_VAR) { + if (pc->p == pc->tstart) { + /* At the start of the token, so just return this char */ + pc->p++; + pc->len--; + } + pc->tend = pc->p - 1; + pc->tt = JIM_TT_ESC; + return JIM_OK; + } + break; + + case '$': + case '[': + pc->tend = pc->p - 1; + pc->tt = JIM_TT_ESC; + return JIM_OK; + case ' ': + case '\t': + case '\n': + case '\r': + case ';': + if (pc->state == JIM_PS_DEF) { + pc->tend = pc->p - 1; + pc->tt = JIM_TT_ESC; + return JIM_OK; + } + else if (*pc->p == '\n') { + pc->linenr++; + } + break; + case '"': + if (pc->state == JIM_PS_QUOTE) { + pc->tend = pc->p - 1; + pc->tt = JIM_TT_ESC; + pc->p++; + pc->len--; + pc->state = JIM_PS_DEF; + return JIM_OK; + } + break; + } + pc->p++; + pc->len--; + } + return JIM_OK; /* unreached */ +} + +static int JimParseComment(struct JimParserCtx *pc) +{ + while (*pc->p) { + if (*pc->p == '\n') { + pc->linenr++; + if (*(pc->p - 1) != '\\') { + pc->p++; + pc->len--; + return JIM_OK; + } + } + pc->p++; + pc->len--; + } + return JIM_OK; +} + +/* xdigitval and odigitval are helper functions for JimEscape() */ +static int xdigitval(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + +static int odigitval(int c) +{ + if (c >= '0' && c <= '7') + return c - '0'; + return -1; +} + +/* Perform Tcl escape substitution of 's', storing the result + * string into 'dest'. The escaped string is guaranteed to + * be the same length or shorted than the source string. + * Slen is the length of the string at 's', if it's -1 the string + * length will be calculated by the function. + * + * The function returns the length of the resulting string. */ +static int JimEscape(char *dest, const char *s, int slen) +{ + char *p = dest; + int i, len; + + if (slen == -1) + slen = strlen(s); + + for (i = 0; i < slen; i++) { + switch (s[i]) { + case '\\': + switch (s[i + 1]) { + case 'a': + *p++ = 0x7; + i++; + break; + case 'b': + *p++ = 0x8; + i++; + break; + case 'f': + *p++ = 0xc; + i++; + break; + case 'n': + *p++ = 0xa; + i++; + break; + case 'r': + *p++ = 0xd; + i++; + break; + case 't': + *p++ = 0x9; + i++; + break; + case 'u': + case 'x': + /* A unicode or hex sequence. + * \u Expect 1-4 hex chars and convert to utf-8. + * \x Expect 1-2 hex chars and convert to hex. + * An invalid sequence means simply the escaped char. + */ + { + int val = 0; + int k; + + i++; + + for (k = 0; k < (s[i] == 'u' ? 4 : 2); k++) { + int c = xdigitval(s[i + k + 1]); + if (c == -1) { + break; + } + val = (val << 4) | c; + } + if (k) { + /* Got a valid sequence, so convert */ + if (s[i] == 'u') { + p += utf8_fromunicode(p, val); + } + else { + *p++ = val; + } + i += k; + break; + } + /* Not a valid codepoint, just an escaped char */ + *p++ = s[i]; + } + break; + case 'v': + *p++ = 0xb; + i++; + break; + case '\0': + *p++ = '\\'; + i++; + break; + case '\n': + /* Replace all spaces and tabs after backslash newline with a single space*/ + *p++ = ' '; + do { + i++; + } while (s[i + 1] == ' ' || s[i + 1] == '\t'); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + /* octal escape */ + { + int val = 0; + int c = odigitval(s[i + 1]); + + val = c; + c = odigitval(s[i + 2]); + if (c == -1) { + *p++ = val; + i++; + break; + } + val = (val * 8) + c; + c = odigitval(s[i + 3]); + if (c == -1) { + *p++ = val; + i += 2; + break; + } + val = (val * 8) + c; + *p++ = val; + i += 3; + } + break; + default: + *p++ = s[i + 1]; + i++; + break; + } + break; + default: + *p++ = s[i]; + break; + } + } + len = p - dest; + *p = '\0'; + return len; +} + +/* Returns a dynamically allocated copy of the current token in the + * parser context. The function performs conversion of escapes if + * the token is of type JIM_TT_ESC. + * + * Note that after the conversion, tokens that are grouped with + * braces in the source code, are always recognizable from the + * identical string obtained in a different way from the type. + * + * For example the string: + * + * {*}$a + * + * will return as first token "*", of type JIM_TT_STR + * + * While the string: + * + * *$a + * + * will return as first token "*", of type JIM_TT_ESC + */ +static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc) +{ + const char *start, *end; + char *token; + int len; + + start = pc->tstart; + end = pc->tend; + if (start > end) { + len = 0; + token = Jim_Alloc(1); + token[0] = '\0'; + } + else { + len = (end - start) + 1; + token = Jim_Alloc(len + 1); + if (pc->tt != JIM_TT_ESC) { + /* No escape conversion needed? Just copy it. */ + memcpy(token, start, len); + token[len] = '\0'; + } + else { + /* Else convert the escape chars. */ + len = JimEscape(token, start, len); + } + } + + return Jim_NewStringObjNoAlloc(interp, token, len); +} + +/* Parses the given string to determine if it represents a complete script. + * + * This is useful for interactive shells implementation, for [info complete]. + * + * If 'stateCharPtr' != NULL, the function stores ' ' on complete script, + * '{' on scripts incomplete missing one or more '}' to be balanced. + * '[' on scripts incomplete missing one or more ']' to be balanced. + * '"' on scripts incomplete missing a '"' char. + * + * If the script is complete, 1 is returned, otherwise 0. + */ +int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr) +{ + struct JimParserCtx parser; + + JimParserInit(&parser, s, len, 1); + while (!parser.eof) { + JimParseScript(&parser); + } + if (stateCharPtr) { + *stateCharPtr = parser.missing; + } + return parser.missing == ' '; +} + +/* ----------------------------------------------------------------------------- + * Tcl Lists parsing + * ---------------------------------------------------------------------------*/ +static int JimParseListSep(struct JimParserCtx *pc); +static int JimParseListStr(struct JimParserCtx *pc); +static int JimParseListQuote(struct JimParserCtx *pc); + +static int JimParseList(struct JimParserCtx *pc) +{ + switch (*pc->p) { + case ' ': + case '\n': + case '\t': + case '\r': + return JimParseListSep(pc); + + case '"': + return JimParseListQuote(pc); + + case '{': + return JimParseBrace(pc); + + default: + if (pc->len) { + return JimParseListStr(pc); + } + break; + } + + pc->tstart = pc->tend = pc->p; + pc->tline = pc->linenr; + pc->tt = JIM_TT_EOL; + pc->eof = 1; + return JIM_OK; +} + +static int JimParseListSep(struct JimParserCtx *pc) +{ + pc->tstart = pc->p; + pc->tline = pc->linenr; + while (*pc->p == ' ' || *pc->p == '\t' || *pc->p == '\r' || *pc->p == '\n') { + if (*pc->p == '\n') { + pc->linenr++; + } + pc->p++; + pc->len--; + } + pc->tend = pc->p - 1; + pc->tt = JIM_TT_SEP; + return JIM_OK; +} + +static int JimParseListQuote(struct JimParserCtx *pc) +{ + pc->p++; + pc->len--; + + pc->tstart = pc->p; + pc->tline = pc->linenr; + pc->tt = JIM_TT_STR; + + while (pc->len) { + switch (*pc->p) { + case '\\': + pc->tt = JIM_TT_ESC; + if (--pc->len == 0) { + /* Trailing backslash */ + pc->tend = pc->p; + return JIM_OK; + } + pc->p++; + break; + case '\n': + pc->linenr++; + break; + case '"': + pc->tend = pc->p - 1; + pc->p++; + pc->len--; + return JIM_OK; + } + pc->p++; + pc->len--; + } + + pc->tend = pc->p - 1; + return JIM_OK; +} + +static int JimParseListStr(struct JimParserCtx *pc) +{ + pc->tstart = pc->p; + pc->tline = pc->linenr; + pc->tt = JIM_TT_STR; + + while (pc->len) { + switch (*pc->p) { + case '\\': + if (--pc->len == 0) { + /* Trailing backslash */ + pc->tend = pc->p; + return JIM_OK; + } + pc->tt = JIM_TT_ESC; + pc->p++; + break; + case ' ': + case '\t': + case '\n': + case '\r': + pc->tend = pc->p - 1; + return JIM_OK; + } + pc->p++; + pc->len--; + } + pc->tend = pc->p - 1; + return JIM_OK; +} + +/* ----------------------------------------------------------------------------- + * Jim_Obj related functions + * ---------------------------------------------------------------------------*/ + +/* Return a new initialized object. */ +Jim_Obj *Jim_NewObj(Jim_Interp *interp) +{ + Jim_Obj *objPtr; + + /* -- Check if there are objects in the free list -- */ + if (interp->freeList != NULL) { + /* -- Unlink the object from the free list -- */ + objPtr = interp->freeList; + interp->freeList = objPtr->nextObjPtr; + } + else { + /* -- No ready to use objects: allocate a new one -- */ + objPtr = Jim_Alloc(sizeof(*objPtr)); + } + + /* Object is returned with refCount of 0. Every + * kind of GC implemented should take care to don't try + * to scan objects with refCount == 0. */ + objPtr->refCount = 0; + /* All the other fields are left not initialized to save time. + * The caller will probably want to set them to the right + * value anyway. */ + + /* -- Put the object into the live list -- */ + objPtr->prevObjPtr = NULL; + objPtr->nextObjPtr = interp->liveList; + if (interp->liveList) + interp->liveList->prevObjPtr = objPtr; + interp->liveList = objPtr; + + return objPtr; +} + +/* Free an object. Actually objects are never freed, but + * just moved to the free objects list, where they will be + * reused by Jim_NewObj(). */ +void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr) +{ + /* Check if the object was already freed, panic. */ + JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr, + objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "")); + + /* Free the internal representation */ + Jim_FreeIntRep(interp, objPtr); + /* Free the string representation */ + if (objPtr->bytes != NULL) { + if (objPtr->bytes != JimEmptyStringRep) + Jim_Free(objPtr->bytes); + } + /* Unlink the object from the live objects list */ + if (objPtr->prevObjPtr) + objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr; + if (objPtr->nextObjPtr) + objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr; + if (interp->liveList == objPtr) + interp->liveList = objPtr->nextObjPtr; + /* Link the object into the free objects list */ + objPtr->prevObjPtr = NULL; + objPtr->nextObjPtr = interp->freeList; + if (interp->freeList) + interp->freeList->prevObjPtr = objPtr; + interp->freeList = objPtr; + objPtr->refCount = -1; +} + +/* Invalidate the string representation of an object. */ +void Jim_InvalidateStringRep(Jim_Obj *objPtr) +{ + if (objPtr->bytes != NULL) { + if (objPtr->bytes != JimEmptyStringRep) + Jim_Free(objPtr->bytes); + } + objPtr->bytes = NULL; +} + +#define Jim_SetStringRep(o, b, l) \ + do { (o)->bytes = b; (o)->length = l; } while (0) + +/* Set the initial string representation for an object. + * Does not try to free an old one. */ +void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length) +{ + if (length == 0) { + objPtr->bytes = JimEmptyStringRep; + objPtr->length = 0; + } + else { + objPtr->bytes = Jim_Alloc(length + 1); + objPtr->length = length; + memcpy(objPtr->bytes, bytes, length); + objPtr->bytes[length] = '\0'; + } +} + +/* Duplicate an object. The returned object has refcount = 0. */ +Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr) +{ + Jim_Obj *dupPtr; + + dupPtr = Jim_NewObj(interp); + if (objPtr->bytes == NULL) { + /* Object does not have a valid string representation. */ + dupPtr->bytes = NULL; + } + else { + Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length); + } + + /* By default, the new object has the same type as the old object */ + dupPtr->typePtr = objPtr->typePtr; + if (objPtr->typePtr != NULL) { + if (objPtr->typePtr->dupIntRepProc == NULL) { + dupPtr->internalRep = objPtr->internalRep; + } + else { + /* The dup proc may set a different type, e.g. NULL */ + objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr); + } + } + return dupPtr; +} + +/* Return the string representation for objPtr. If the object + * string representation is invalid, calls the method to create + * a new one starting from the internal representation of the object. */ +const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr) +{ + if (objPtr->bytes == NULL) { + /* Invalid string repr. Generate it. */ + JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name)); + objPtr->typePtr->updateStringProc(objPtr); + } + if (lenPtr) + *lenPtr = objPtr->length; + return objPtr->bytes; +} + +/* Just returns the length of the object's string rep */ +int Jim_Length(Jim_Obj *objPtr) +{ + int len; + + Jim_GetString(objPtr, &len); + return len; +} + +static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); +static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); + +static const Jim_ObjType dictSubstObjType = { + "dict-substitution", + FreeDictSubstInternalRep, + DupDictSubstInternalRep, + NULL, + JIM_TYPE_NONE, +}; + +static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) +{ + Jim_DecrRefCount(interp, (Jim_Obj *)objPtr->internalRep.twoPtrValue.ptr2); +} + +static const Jim_ObjType interpolatedObjType = { + "interpolated", + FreeInterpolatedInternalRep, + NULL, + NULL, + JIM_TYPE_NONE, +}; + +/* ----------------------------------------------------------------------------- + * String Object + * ---------------------------------------------------------------------------*/ +static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); +static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); + +static const Jim_ObjType stringObjType = { + "string", + NULL, + DupStringInternalRep, + NULL, + JIM_TYPE_REFERENCES, +}; + +static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) +{ + JIM_NOTUSED(interp); + + /* This is a bit subtle: the only caller of this function + * should be Jim_DuplicateObj(), that will copy the + * string representaion. After the copy, the duplicated + * object will not have more room in teh buffer than + * srcPtr->length bytes. So we just set it to length. */ + dupPtr->internalRep.strValue.maxLength = srcPtr->length; + + dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength; +} + +static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr) +{ + /* Get a fresh string representation. */ + (void)Jim_String(objPtr); + /* Free any other internal representation. */ + Jim_FreeIntRep(interp, objPtr); + /* Set it as string, i.e. just set the maxLength field. */ + objPtr->typePtr = &stringObjType; + objPtr->internalRep.strValue.maxLength = objPtr->length; + /* Don't know the utf-8 length yet */ + objPtr->internalRep.strValue.charLength = -1; + return JIM_OK; +} + +/** + * Returns the length of the object string in chars, not bytes. + * + * These may be different for a utf-8 string. + */ +int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr) +{ +#ifdef JIM_UTF8 + if (objPtr->typePtr != &stringObjType) + SetStringFromAny(interp, objPtr); + + if (objPtr->internalRep.strValue.charLength < 0) { + objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length); + } + return objPtr->internalRep.strValue.charLength; +#else + return Jim_Length(objPtr); +#endif +} + +/* len is in bytes -- see also Jim_NewStringObjUtf8() */ +Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len) +{ + Jim_Obj *objPtr = Jim_NewObj(interp); + + /* Need to find out how many bytes the string requires */ + if (len == -1) + len = strlen(s); + /* Alloc/Set the string rep. */ + if (len == 0) { + objPtr->bytes = JimEmptyStringRep; + objPtr->length = 0; + } + else { + objPtr->bytes = Jim_Alloc(len + 1); + objPtr->length = len; + memcpy(objPtr->bytes, s, len); + objPtr->bytes[len] = '\0'; + } + + /* No typePtr field for the vanilla string object. */ + objPtr->typePtr = NULL; + return objPtr; +} + +/* charlen is in characters -- see also Jim_NewStringObj() */ +Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen) +{ +#ifdef JIM_UTF8 + /* Need to find out how many bytes the string requires */ + int bytelen = utf8_index(s, charlen); + + Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen); + + /* Remember the utf8 length, so set the type */ + objPtr->typePtr = &stringObjType; + objPtr->internalRep.strValue.maxLength = bytelen; + objPtr->internalRep.strValue.charLength = charlen; + + return objPtr; +#else + return Jim_NewStringObj(interp, s, charlen); +#endif +} + +/* This version does not try to duplicate the 's' pointer, but + * use it directly. */ +Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len) +{ + Jim_Obj *objPtr = Jim_NewObj(interp); + + if (len == -1) + len = strlen(s); + Jim_SetStringRep(objPtr, s, len); + objPtr->typePtr = NULL; + return objPtr; +} + +/* Low-level string append. Use it only against objects + * of type "string". */ +static void StringAppendString(Jim_Obj *objPtr, const char *str, int len) +{ + int needlen; + + if (len == -1) + len = strlen(str); + needlen = objPtr->length + len; + if (objPtr->internalRep.strValue.maxLength < needlen || + objPtr->internalRep.strValue.maxLength == 0) { + needlen *= 2; + /* Inefficient to malloc() for less than 8 bytes */ + if (needlen < 7) { + needlen = 7; + } + if (objPtr->bytes == JimEmptyStringRep) { + objPtr->bytes = Jim_Alloc(needlen + 1); + } + else { + objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1); + } + objPtr->internalRep.strValue.maxLength = needlen; + } + memcpy(objPtr->bytes + objPtr->length, str, len); + objPtr->bytes[objPtr->length + len] = '\0'; + if (objPtr->internalRep.strValue.charLength >= 0) { + /* Update the utf-8 char length */ + objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len); + } + objPtr->length += len; +} + +/* Higher level API to append strings to objects. */ +void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len) +{ + JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object")); + if (objPtr->typePtr != &stringObjType) + SetStringFromAny(interp, objPtr); + StringAppendString(objPtr, str, len); +} + +void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr) +{ + int len; + const char *str; + + str = Jim_GetString(appendObjPtr, &len); + Jim_AppendString(interp, objPtr, str, len); +} + +void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...) +{ + va_list ap; + + if (objPtr->typePtr != &stringObjType) + SetStringFromAny(interp, objPtr); + va_start(ap, objPtr); + while (1) { + char *s = va_arg(ap, char *); + + if (s == NULL) + break; + Jim_AppendString(interp, objPtr, s, -1); + } + va_end(ap); +} + +int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr) +{ + const char *aStr, *bStr; + int aLen, bLen; + + if (aObjPtr == bObjPtr) + return 1; + aStr = Jim_GetString(aObjPtr, &aLen); + bStr = Jim_GetString(bObjPtr, &bLen); + if (aLen != bLen) + return 0; + return JimStringCompare(aStr, aLen, bStr, bLen) == 0; +} + +int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase) +{ + return JimStringMatch(interp, patternObjPtr, Jim_String(objPtr), nocase); +} + +int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase) +{ + const char *s1, *s2; + int l1, l2; + + s1 = Jim_GetString(firstObjPtr, &l1); + s2 = Jim_GetString(secondObjPtr, &l2); + + if (nocase) { + return JimStringCompareNoCase(s1, s2, -1); + } + return JimStringCompare(s1, l1, s2, l2); +} + +/* Convert a range, as returned by Jim_GetRange(), into + * an absolute index into an object of the specified length. + * This function may return negative values, or values + * bigger or equal to the length of the list if the index + * is out of range. */ +static int JimRelToAbsIndex(int len, int idx) +{ + if (idx < 0) + return len + idx; + return idx; +} + +/* Convert a pair of index as normalize by JimRelToAbsIndex(), + * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable + * for implementation of commands like [string range] and [lrange]. + * + * The resulting range is guaranteed to address valid elements of + * the structure. */ +static void JimRelToAbsRange(int len, int first, int last, + int *firstPtr, int *lastPtr, int *rangeLenPtr) +{ + int rangeLen; + + if (first > last) { + rangeLen = 0; + } + else { + rangeLen = last - first + 1; + if (rangeLen) { + if (first < 0) { + rangeLen += first; + first = 0; + } + if (last >= len) { + rangeLen -= (last - (len - 1)); + last = len - 1; + } + } + } + if (rangeLen < 0) + rangeLen = 0; + + *firstPtr = first; + *lastPtr = last; + *rangeLenPtr = rangeLen; +} + +Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp, + Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr) +{ + int first, last; + const char *str; + int rangeLen; + int bytelen; + + if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK || + Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK) + return NULL; + str = Jim_GetString(strObjPtr, &bytelen); + first = JimRelToAbsIndex(bytelen, first); + last = JimRelToAbsIndex(bytelen, last); + JimRelToAbsRange(bytelen, first, last, &first, &last, &rangeLen); + if (first == 0 && rangeLen == bytelen) { + return strObjPtr; + } + return Jim_NewStringObj(interp, str + first, rangeLen); +} + +Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp, + Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr) +{ +#ifdef JIM_UTF8 + int first, last; + const char *str; + int len, rangeLen; + int bytelen; + + if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK || + Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK) + return NULL; + str = Jim_GetString(strObjPtr, &bytelen); + len = Jim_Utf8Length(interp, strObjPtr); + first = JimRelToAbsIndex(len, first); + last = JimRelToAbsIndex(len, last); + JimRelToAbsRange(len, first, last, &first, &last, &rangeLen); + if (first == 0 && rangeLen == len) { + return strObjPtr; + } + if (len == bytelen) { + /* ASCII optimisation */ + return Jim_NewStringObj(interp, str + first, rangeLen); + } + return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen); +#else + return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr); +#endif +} + +static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr) +{ + char *buf, *p; + int len; + const char *str; + + if (strObjPtr->typePtr != &stringObjType) { + SetStringFromAny(interp, strObjPtr); + } + + str = Jim_GetString(strObjPtr, &len); + + buf = p = Jim_Alloc(len + 1); + while (*str) { + int c; + str += utf8_tounicode(str, &c); + p += utf8_fromunicode(p, utf8_lower(c)); + } + *p = 0; + return Jim_NewStringObjNoAlloc(interp, buf, len); +} + +static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr) +{ + char *buf, *p; + int len; + const char *str; + + if (strObjPtr->typePtr != &stringObjType) { + SetStringFromAny(interp, strObjPtr); + } + + str = Jim_GetString(strObjPtr, &len); + + buf = p = Jim_Alloc(len + 1); + while (*str) { + int c; + str += utf8_tounicode(str, &c); + p += utf8_fromunicode(p, utf8_upper(c)); + } + *p = 0; + return Jim_NewStringObjNoAlloc(interp, buf, len); +} + +/* Similar to memchr() except searches a UTF-8 string 'str' of byte length 'len' + * for unicode character 'c'. + * Returns the position if found or NULL if not + */ +static const char *utf8_memchr(const char *str, int len, int c) +{ +#ifdef JIM_UTF8 + while (len) { + int sc; + int n = utf8_tounicode(str, &sc); + if (sc == c) { + return str; + } + str += n; + len -= n; + } + return NULL; +#else + return memchr(str, c, len); +#endif +} + +/** + * Searches for the first non-trim char in string (str, len) + * + * If none is found, returns just past the last char. + * + * Lengths are in bytes. + */ +static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen) +{ + while (len) { + int c; + int n = utf8_tounicode(str, &c); + + if (utf8_memchr(trimchars, trimlen, c) == NULL) { + /* Not a trim char, so stop */ + break; + } + str += n; + len -= n; + } + return str; +} + +/** + * Searches backwards for a non-trim char in string (str, len). + * + * Returns a pointer to just after the non-trim char, or NULL if not found. + * + * Lengths are in bytes. + */ +static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen) +{ + str += len; + + while (len) { + int c; + int n = utf8_prev_len(str, len); + + len -= n; + str -= n; + + n = utf8_tounicode(str, &c); + + if (utf8_memchr(trimchars, trimlen, c) == NULL) { + return str + n; + } + } + + return NULL; +} + +static const char default_trim_chars[] = " \t\n\r"; +/* sizeof() here includes the null byte */ +static int default_trim_chars_len = sizeof(default_trim_chars); + +static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr) +{ + int len; + const char *str = Jim_GetString(strObjPtr, &len); + const char *trimchars = default_trim_chars; + int trimcharslen = default_trim_chars_len; + const char *newstr; + + if (trimcharsObjPtr) { + trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen); + } + + newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen); + if (newstr == str) { + return strObjPtr; + } + + return Jim_NewStringObj(interp, newstr, len - (newstr - str)); +} + +static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr) +{ + int len; + const char *trimchars = default_trim_chars; + int trimcharslen = default_trim_chars_len; + const char *nontrim; + + if (trimcharsObjPtr) { + trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen); + } + + if (strObjPtr->typePtr != &stringObjType) { + SetStringFromAny(interp, strObjPtr); + } + len = Jim_Length(strObjPtr); + nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen); + + if (nontrim == NULL) { + /* All trim, so return a zero-length string */ + return Jim_NewEmptyStringObj(interp); + } + if (nontrim == strObjPtr->bytes + len) { + return strObjPtr; + } + + if (Jim_IsShared(strObjPtr)) { + strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes)); + } + else { + /* Can modify this string in place */ + strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0; + strObjPtr->length = (nontrim - strObjPtr->bytes); + } + + return strObjPtr; +} + +static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr) +{ + /* First trim left. */ + Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr); + + /* Now trim right */ + strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr); + + if (objPtr != strObjPtr) { + /* Note that we don't want this object to be leaked */ + Jim_IncrRefCount(objPtr); + Jim_DecrRefCount(interp, objPtr); + } + + return strObjPtr; +} + + +static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict) +{ + static const char * const strclassnames[] = { + "integer", "alpha", "alnum", "ascii", "digit", + "double", "lower", "upper", "space", "xdigit", + "control", "print", "graph", "punct", + NULL + }; + enum { + STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT, + STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT, + STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT + }; + int strclass; + int len; + int i; + const char *str; + int (*isclassfunc)(int c) = NULL; + + if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { + return JIM_ERR; + } + + str = Jim_GetString(strObjPtr, &len); + if (len == 0) { + Jim_SetResultInt(interp, !strict); + return JIM_OK; + } + + switch (strclass) { + case STR_IS_INTEGER: + { + jim_wide w; + Jim_SetResultInt(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK); + return JIM_OK; + } + + case STR_IS_DOUBLE: + { + double d; + Jim_SetResultInt(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE); + return JIM_OK; + } + + case STR_IS_ALPHA: isclassfunc = isalpha; break; + case STR_IS_ALNUM: isclassfunc = isalnum; break; + case STR_IS_ASCII: isclassfunc = isascii; break; + case STR_IS_DIGIT: isclassfunc = isdigit; break; + case STR_IS_LOWER: isclassfunc = islower; break; + case STR_IS_UPPER: isclassfunc = isupper; break; + case STR_IS_SPACE: isclassfunc = isspace; break; + case STR_IS_XDIGIT: isclassfunc = isxdigit; break; + case STR_IS_CONTROL: isclassfunc = iscntrl; break; + case STR_IS_PRINT: isclassfunc = isprint; break; + case STR_IS_GRAPH: isclassfunc = isgraph; break; + case STR_IS_PUNCT: isclassfunc = ispunct; break; + default: + return JIM_ERR; + } + + for (i = 0; i < len; i++) { + if (!isclassfunc(str[i])) { + Jim_SetResultInt(interp, 0); + return JIM_OK; + } + } + Jim_SetResultInt(interp, 1); + return JIM_OK; +} + +/* ----------------------------------------------------------------------------- + * Compared String Object + * ---------------------------------------------------------------------------*/ + +/* This is strange object that allows to compare a C literal string + * with a Jim object in very short time if the same comparison is done + * multiple times. For example every time the [if] command is executed, + * Jim has to check if a given argument is "else". This comparions if + * the code has no errors are true most of the times, so we can cache + * inside the object the pointer of the string of the last matching + * comparison. Because most C compilers perform literal sharing, + * so that: char *x = "foo", char *y = "foo", will lead to x == y, + * this works pretty well even if comparisons are at different places + * inside the C code. */ + +static const Jim_ObjType comparedStringObjType = { + "compared-string", + NULL, + NULL, + NULL, + JIM_TYPE_REFERENCES, +}; + +/* The only way this object is exposed to the API is via the following + * function. Returns true if the string and the object string repr. + * are the same, otherwise zero is returned. + * + * Note: this isn't binary safe, but it hardly needs to be.*/ +int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str) +{ + if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) + return 1; + else { + const char *objStr = Jim_String(objPtr); + + if (strcmp(str, objStr) != 0) + return 0; + if (objPtr->typePtr != &comparedStringObjType) { + Jim_FreeIntRep(interp, objPtr); + objPtr->typePtr = &comparedStringObjType; + } + objPtr->internalRep.ptr = (char *)str; /*ATTENTION: const cast */ + return 1; + } +} + +static int qsortCompareStringPointers(const void *a, const void *b) +{ + char *const *sa = (char *const *)a; + char *const *sb = (char *const *)b; + + return strcmp(*sa, *sb); +} + + +/* ----------------------------------------------------------------------------- + * Source Object + * + * This object is just a string from the language point of view, but + * in the internal representation it contains the filename and line number + * where this given token was read. This information is used by + * Jim_EvalObj() if the object passed happens to be of type "source". + * + * This allows to propagate the information about line numbers and file + * names and give error messages with absolute line numbers. + * + * Note that this object uses shared strings for filenames, and the + * pointer to the filename together with the line number is taken into + * the space for the "inline" internal representation of the Jim_Object, + * so there is almost memory zero-overhead. + * + * Also the object will be converted to something else if the given + * token it represents in the source file is not something to be + * evaluated (not a script), and will be specialized in some other way, + * so the time overhead is also null. + * ---------------------------------------------------------------------------*/ + +static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); +static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); + +static const Jim_ObjType sourceObjType = { + "source", + FreeSourceInternalRep, + DupSourceInternalRep, + NULL, + JIM_TYPE_REFERENCES, +}; + +void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) +{ + Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj); +} + +void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) +{ + dupPtr->internalRep = srcPtr->internalRep; + Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj); +} + +static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, + Jim_Obj *fileNameObj, int lineNumber) +{ + JimPanic((Jim_IsShared(objPtr), "JimSetSourceInfo called with shared object")); + JimPanic((objPtr->typePtr != NULL, "JimSetSourceInfo called with typePtr != NULL")); + Jim_IncrRefCount(fileNameObj); + objPtr->internalRep.sourceValue.fileNameObj = fileNameObj; + objPtr->internalRep.sourceValue.lineNumber = lineNumber; + objPtr->typePtr = &sourceObjType; +} + +/* ----------------------------------------------------------------------------- + * Script Object + * ---------------------------------------------------------------------------*/ + +static const Jim_ObjType scriptLineObjType = { + "scriptline", + NULL, + NULL, + NULL, + 0, +}; + +static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line) +{ + Jim_Obj *objPtr; + +#ifdef DEBUG_SHOW_SCRIPT + char buf[100]; + snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc); + objPtr = Jim_NewStringObj(interp, buf, -1); +#else + objPtr = Jim_NewEmptyStringObj(interp); +#endif + objPtr->typePtr = &scriptLineObjType; + objPtr->internalRep.scriptLineValue.argc = argc; + objPtr->internalRep.scriptLineValue.line = line; + + return objPtr; +} + +#define JIM_CMDSTRUCT_EXPAND -1 + +static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); +static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); +static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, struct JimParseResult *result); + +static const Jim_ObjType scriptObjType = { + "script", + FreeScriptInternalRep, + DupScriptInternalRep, + NULL, + JIM_TYPE_REFERENCES, +}; + +/* The ScriptToken structure represents every token into a scriptObj. + * Every token contains an associated Jim_Obj that can be specialized + * by commands operating on it. */ +typedef struct ScriptToken +{ + int type; + Jim_Obj *objPtr; +} ScriptToken; + +/* This is the script object internal representation. An array of + * ScriptToken structures, including a pre-computed representation of the + * command length and arguments. + * + * For example the script: + * + * puts hello + * set $i $x$y [foo]BAR + * + * will produce a ScriptObj with the following Tokens: + * + * LIN 2 + * ESC puts + * ESC hello + * LIN 4 + * ESC set + * VAR i + * WRD 2 + * VAR x + * VAR y + * WRD 2 + * CMD foo + * ESC BAR + * + * "puts hello" has two args (LIN 2), composed of single tokens. + * (Note that the WRD token is omitted for the common case of a single token.) + * + * "set $i $x$y [foo]BAR" has four (LIN 4) args, the first word + * has 1 token (ESC SET), and the last has two tokens (WRD 2 CMD foo ESC BAR) + * + * The precomputation of the command structure makes Jim_Eval() faster, + * and simpler because there aren't dynamic lengths / allocations. + * + * -- {expand}/{*} handling -- + * + * Expand is handled in a special way. + * + * If a "word" begins with {*}, the word token count is -ve. + * + * For example the command: + * + * list {*}{a b} + * + * Will produce the following cmdstruct array: + * + * LIN 2 + * ESC list + * WRD -1 + * STR a b + * + * Note that the 'LIN' token also contains the source information for the + * first word of the line for error reporting purposes + * + * -- the substFlags field of the structure -- + * + * The scriptObj structure is used to represent both "script" objects + * and "subst" objects. In the second case, the there are no LIN and WRD + * tokens. Instead SEP and EOL tokens are added as-is. + * In addition, the field 'substFlags' is used to represent the flags used to turn + * the string into the internal representation used to perform the + * substitution. If this flags are not what the application requires + * the scriptObj is created again. For example the script: + * + * subst -nocommands $string + * subst -novariables $string + * + * Will recreate the internal representation of the $string object + * two times. + */ +typedef struct ScriptObj +{ + int len; /* Length as number of tokens. */ + ScriptToken *token; /* Tokens array. */ + int substFlags; /* flags used for the compilation of "subst" objects */ + int inUse; /* Used to share a ScriptObj. Currently + only used by Jim_EvalObj() as protection against + shimmering of the currently evaluated object. */ + Jim_Obj *fileNameObj; + int line; /* Line number of the first line */ +} ScriptObj; + +void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) +{ + int i; + struct ScriptObj *script = (void *)objPtr->internalRep.ptr; + + script->inUse--; + if (script->inUse != 0) + return; + for (i = 0; i < script->len; i++) { + Jim_DecrRefCount(interp, script->token[i].objPtr); + } + Jim_Free(script->token); + Jim_DecrRefCount(interp, script->fileNameObj); + Jim_Free(script); +} + +void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) +{ + JIM_NOTUSED(interp); + JIM_NOTUSED(srcPtr); + + /* Just returns an simple string. */ + dupPtr->typePtr = NULL; +} + +/* A simple parser token. + * All the simple tokens for the script point into the same script string rep. + */ +typedef struct +{ + const char *token; /* Pointer to the start of the token */ + int len; /* Length of this token */ + int type; /* Token type */ + int line; /* Line number */ +} ParseToken; + +/* A list of parsed tokens representing a script. + * Tokens are added to this list as the script is parsed. + * It grows as needed. + */ +typedef struct +{ + /* Start with a statically allocated list of tokens which will be expanded with realloc if needed */ + ParseToken *list; /* Array of tokens */ + int size; /* Current size of the list */ + int count; /* Number of entries used */ + ParseToken static_list[20]; /* Small initial token space to avoid allocation */ +} ParseTokenList; + +static void ScriptTokenListInit(ParseTokenList *tokenlist) +{ + tokenlist->list = tokenlist->static_list; + tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken); + tokenlist->count = 0; +} + +static void ScriptTokenListFree(ParseTokenList *tokenlist) +{ + if (tokenlist->list != tokenlist->static_list) { + Jim_Free(tokenlist->list); + } +} + +/** + * Adds the new token to the tokenlist. + * The token has the given length, type and line number. + * The token list is resized as necessary. + */ +static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type, + int line) +{ + ParseToken *t; + + if (tokenlist->count == tokenlist->size) { + /* Resize the list */ + tokenlist->size *= 2; + if (tokenlist->list != tokenlist->static_list) { + tokenlist->list = + Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list)); + } + else { + /* The list needs to become allocated */ + tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list)); + memcpy(tokenlist->list, tokenlist->static_list, + tokenlist->count * sizeof(*tokenlist->list)); + } + } + t = &tokenlist->list[tokenlist->count++]; + t->token = token; + t->len = len; + t->type = type; + t->line = line; +} + +/* Counts the number of adjoining non-separator. + * + * Returns -ve if the first token is the expansion + * operator (in which case the count doesn't include + * that token). + */ +static int JimCountWordTokens(ParseToken *t) +{ + int expand = 1; + int count = 0; + + /* Is the first word {*} or {expand}? */ + if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) { + if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) { + /* Create an expand token */ + expand = -1; + t++; + } + } + + /* Now count non-separator words */ + while (!TOKEN_IS_SEP(t->type)) { + t++; + count++; + } + + return count * expand; +} + +/** + * Create a script/subst object from the given token. + */ +static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t) +{ + Jim_Obj *objPtr; + + if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) { + /* Convert the backlash escapes . */ + int len = t->len; + char *str = Jim_Alloc(len + 1); + len = JimEscape(str, t->token, len); + objPtr = Jim_NewStringObjNoAlloc(interp, str, len); + } + else { + /* REVIST: Strictly, JIM_TT_STR should replace + * with a single space. This is currently not done. + */ + objPtr = Jim_NewStringObj(interp, t->token, t->len); + } + return objPtr; +} + +/** + * Takes a tokenlist and creates the allocated list of script tokens + * in script->token, of length script->len. + * + * Unnecessary tokens are discarded, and LINE and WORD tokens are inserted + * as required. + * + * Also sets script->line to the line number of the first token + */ +static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script, + ParseTokenList *tokenlist) +{ + int i; + struct ScriptToken *token; + /* Number of tokens so far for the current command */ + int lineargs = 0; + /* This is the first token for the current command */ + ScriptToken *linefirst; + int count; + int linenr; + +#ifdef DEBUG_SHOW_SCRIPT_TOKENS + printf("==== Tokens ====\n"); + for (i = 0; i < tokenlist->count; i++) { + printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type), + tokenlist->list[i].len, tokenlist->list[i].token); + } +#endif + + /* May need up to one extra script token for each EOL in the worst case */ + count = tokenlist->count; + for (i = 0; i < tokenlist->count; i++) { + if (tokenlist->list[i].type == JIM_TT_EOL) { + count++; + } + } + linenr = script->line = tokenlist->list[0].line; + + token = script->token = Jim_Alloc(sizeof(ScriptToken) * count); + + /* This is the first token for the current command */ + linefirst = token++; + + for (i = 0; i < tokenlist->count; ) { + /* Look ahead to find out how many tokens make up the next word */ + int wordtokens; + + /* Skip any leading separators */ + while (tokenlist->list[i].type == JIM_TT_SEP) { + i++; + } + + wordtokens = JimCountWordTokens(tokenlist->list + i); + + if (wordtokens == 0) { + /* None, so at end of line */ + if (lineargs) { + linefirst->type = JIM_TT_LINE; + linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr); + Jim_IncrRefCount(linefirst->objPtr); + + /* Reset for new line */ + lineargs = 0; + linefirst = token++; + } + i++; + continue; + } + else if (wordtokens != 1) { + /* More than 1, or {expand}, so insert a WORD token */ + token->type = JIM_TT_WORD; + token->objPtr = Jim_NewIntObj(interp, wordtokens); + Jim_IncrRefCount(token->objPtr); + token++; + if (wordtokens < 0) { + /* Skip the expand token */ + i++; + wordtokens = -wordtokens - 1; + lineargs--; + } + } + + if (lineargs == 0) { + /* First real token on the line, so record the line number */ + linenr = tokenlist->list[i].line; + } + lineargs++; + + /* Add each non-separator word token to the line */ + while (wordtokens--) { + const ParseToken *t = &tokenlist->list[i++]; + + token->type = t->type; + token->objPtr = JimMakeScriptObj(interp, t); + Jim_IncrRefCount(token->objPtr); + + /* Every object is initially a string, but the + * internal type may be specialized during execution of the + * script. */ + JimSetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line); + token++; + } + } + + if (lineargs == 0) { + token--; + } + + script->len = token - script->token; + + assert(script->len < count); + +#ifdef DEBUG_SHOW_SCRIPT + printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj)); + for (i = 0; i < script->len; i++) { + const ScriptToken *t = &script->token[i]; + printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr)); + } +#endif + +} + +/** + * Similar to ScriptObjAddTokens(), but for subst objects. + */ +static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script, + ParseTokenList *tokenlist) +{ + int i; + struct ScriptToken *token; + + token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count); + + for (i = 0; i < tokenlist->count; i++) { + const ParseToken *t = &tokenlist->list[i]; + + /* Create a token for 't' */ + token->type = t->type; + token->objPtr = JimMakeScriptObj(interp, t); + Jim_IncrRefCount(token->objPtr); + token++; + } + + script->len = i; +} + +/* This method takes the string representation of an object + * as a Tcl script, and generates the pre-parsed internal representation + * of the script. */ +static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, struct JimParseResult *result) +{ + int scriptTextLen; + const char *scriptText = Jim_GetString(objPtr, &scriptTextLen); + struct JimParserCtx parser; + struct ScriptObj *script; + ParseTokenList tokenlist; + int line = 1; + + /* Try to get information about filename / line number */ + if (objPtr->typePtr == &sourceObjType) { + line = objPtr->internalRep.sourceValue.lineNumber; + } + + /* Initially parse the script into tokens (in tokenlist) */ + ScriptTokenListInit(&tokenlist); + + JimParserInit(&parser, scriptText, scriptTextLen, line); + while (!parser.eof) { + JimParseScript(&parser); + ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, + parser.tline); + } + if (result && parser.missing != ' ') { + ScriptTokenListFree(&tokenlist); + result->missing = parser.missing; + result->line = parser.missingline; + return JIM_ERR; + } + + /* Add a final EOF token */ + ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0); + + /* Create the "real" script tokens from the initial token list */ + script = Jim_Alloc(sizeof(*script)); + memset(script, 0, sizeof(*script)); + script->inUse = 1; + script->line = line; + if (objPtr->typePtr == &sourceObjType) { + script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj; + } + else { + script->fileNameObj = interp->emptyObj; + } + Jim_IncrRefCount(script->fileNameObj); + + ScriptObjAddTokens(interp, script, &tokenlist); + + /* No longer need the token list */ + ScriptTokenListFree(&tokenlist); + + /* Free the old internal rep and set the new one. */ + Jim_FreeIntRep(interp, objPtr); + Jim_SetIntRepPtr(objPtr, script); + objPtr->typePtr = &scriptObjType; + + return JIM_OK; +} + +ScriptObj *Jim_GetScript(Jim_Interp *interp, Jim_Obj *objPtr) +{ + struct ScriptObj *script = Jim_GetIntRepPtr(objPtr); + + if (objPtr->typePtr != &scriptObjType || script->substFlags) { + SetScriptFromAny(interp, objPtr, NULL); + } + return (ScriptObj *) Jim_GetIntRepPtr(objPtr); +} + +/* ----------------------------------------------------------------------------- + * Commands + * ---------------------------------------------------------------------------*/ +static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr) +{ + cmdPtr->inUse++; +} + +static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr) +{ + if (--cmdPtr->inUse == 0) { + if (cmdPtr->isproc) { + Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr); + Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr); + if (cmdPtr->u.proc.staticVars) { + Jim_FreeHashTable(cmdPtr->u.proc.staticVars); + Jim_Free(cmdPtr->u.proc.staticVars); + } + if (cmdPtr->u.proc.prevCmd) { + /* Delete any pushed command too */ + JimDecrCmdRefCount(interp, cmdPtr->u.proc.prevCmd); + } + } + else { + /* native (C) */ + if (cmdPtr->u.native.delProc) { + cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData); + } + } + Jim_Free(cmdPtr); + } +} + +/* Commands HashTable Type. + * + * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */ +static void JimCommandsHT_ValDestructor(void *interp, void *val) +{ + JimDecrCmdRefCount(interp, val); +} + +static const Jim_HashTableType JimCommandsHashTableType = { + JimStringCopyHTHashFunction, /* hash function */ + JimStringCopyHTKeyDup, /* key dup */ + NULL, /* val dup */ + JimStringCopyHTKeyCompare, /* key compare */ + JimStringCopyHTKeyDestructor, /* key destructor */ + JimCommandsHT_ValDestructor /* val destructor */ +}; + +/* ------------------------- Commands related functions --------------------- */ + +int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName, + Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc) +{ + Jim_Cmd *cmdPtr; + + if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) { + /* Command existed so incr proc epoch */ + Jim_InterpIncrProcEpoch(interp); + } + + cmdPtr = Jim_Alloc(sizeof(*cmdPtr)); + + /* Store the new details for this proc */ + memset(cmdPtr, 0, sizeof(*cmdPtr)); + cmdPtr->inUse = 1; + cmdPtr->u.native.delProc = delProc; + cmdPtr->u.native.cmdProc = cmdProc; + cmdPtr->u.native.privData = privData; + + Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr); + + /* There is no need to increment the 'proc epoch' because + * creation of a new procedure can never affect existing + * cached commands. We don't do negative caching. */ + return JIM_OK; +} + +static int JimCreateProcedure(Jim_Interp *interp, Jim_Obj *cmdName, + Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr) +{ + Jim_Cmd *cmdPtr; + Jim_HashEntry *he; + int argListLen; + int i; + + if (JimValidName(interp, "procedure", cmdName) != JIM_OK) { + return JIM_ERR; + } + + argListLen = Jim_ListLength(interp, argListObjPtr); + + /* Allocate space for both the command pointer and the arg list */ + cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen); + memset(cmdPtr, 0, sizeof(*cmdPtr)); + cmdPtr->inUse = 1; + cmdPtr->isproc = 1; + cmdPtr->u.proc.argListObjPtr = argListObjPtr; + cmdPtr->u.proc.argListLen = argListLen; + cmdPtr->u.proc.bodyObjPtr = bodyObjPtr; + cmdPtr->u.proc.argsPos = -1; + cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1); + Jim_IncrRefCount(argListObjPtr); + Jim_IncrRefCount(bodyObjPtr); + + /* Create the statics hash table. */ + if (staticsListObjPtr) { + int len, i; + + len = Jim_ListLength(interp, staticsListObjPtr); + if (len != 0) { + cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable)); + Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp); + for (i = 0; i < len; i++) { + Jim_Obj *objPtr = 0, *initObjPtr = 0, *nameObjPtr = 0; + Jim_Var *varPtr; + int subLen; + + Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE); + /* Check if it's composed of two elements. */ + subLen = Jim_ListLength(interp, objPtr); + if (subLen == 1 || subLen == 2) { + /* Try to get the variable value from the current + * environment. */ + Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE); + if (subLen == 1) { + initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE); + if (initObjPtr == NULL) { + Jim_SetResultFormatted(interp, + "variable for initialization of static \"%#s\" not found in the local context", + nameObjPtr); + goto err; + } + } + else { + Jim_ListIndex(interp, objPtr, 1, &initObjPtr, JIM_NONE); + } + if (JimValidName(interp, "static variable", nameObjPtr) != JIM_OK) { + goto err; + } + + varPtr = Jim_Alloc(sizeof(*varPtr)); + varPtr->objPtr = initObjPtr; + Jim_IncrRefCount(initObjPtr); + varPtr->linkFramePtr = NULL; + if (Jim_AddHashEntry(cmdPtr->u.proc.staticVars, + Jim_String(nameObjPtr), varPtr) != JIM_OK) { + Jim_SetResultFormatted(interp, + "static variable name \"%#s\" duplicated in statics list", nameObjPtr); + Jim_DecrRefCount(interp, initObjPtr); + Jim_Free(varPtr); + goto err; + } + } + else { + Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"", + objPtr); + goto err; + } + } + } + } + + /* Parse the args out into arglist, validating as we go */ + /* Examine the argument list for default parameters and 'args' */ + for (i = 0; i < argListLen; i++) { + Jim_Obj *argPtr; + Jim_Obj *nameObjPtr; + Jim_Obj *defaultObjPtr; + int len; + int n = 1; + + /* Examine a parameter */ + Jim_ListIndex(interp, argListObjPtr, i, &argPtr, JIM_NONE); + len = Jim_ListLength(interp, argPtr); + if (len == 0) { + Jim_SetResultString(interp, "procedure has argument with no name", -1); + goto err; + } + if (len > 2) { + Jim_SetResultString(interp, "procedure has argument with too many fields", -1); + goto err; + } + + if (len == 2) { + /* Optional parameter */ + Jim_ListIndex(interp, argPtr, 0, &nameObjPtr, JIM_NONE); + Jim_ListIndex(interp, argPtr, 1, &defaultObjPtr, JIM_NONE); + } + else { + /* Required parameter */ + nameObjPtr = argPtr; + defaultObjPtr = NULL; + } + + + if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) { + if (cmdPtr->u.proc.argsPos >= 0) { + Jim_SetResultString(interp, "procedure has 'args' specified more than once", -1); + goto err; + } + cmdPtr->u.proc.argsPos = i; + } + else { + if (len == 2) { + cmdPtr->u.proc.optArity += n; + } + else { + cmdPtr->u.proc.reqArity += n; + } + } + + cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr; + cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr; + } + + /* Add the new command */ + + /* It may already exist, so we try to delete the old one. + * Note that reference count means that it won't be deleted yet if + * it exists in the call stack. + * + * BUT, if 'local' is in force, instead of deleting the existing + * proc, we stash a reference to the old proc here. + */ + he = Jim_FindHashEntry(&interp->commands, Jim_String(cmdName)); + if (he) { + /* There was an old procedure with the same name, this requires + * a 'proc epoch' update. */ + + /* If a procedure with the same name didn't existed there is no need + * to increment the 'proc epoch' because creation of a new procedure + * can never affect existing cached commands. We don't do + * negative caching. */ + Jim_InterpIncrProcEpoch(interp); + } + + if (he && interp->local) { + /* Just push this proc over the top of the previous one */ + cmdPtr->u.proc.prevCmd = he->u.val; + he->u.val = cmdPtr; + } + else { + if (he) { + /* Replace the existing proc */ + Jim_DeleteHashEntry(&interp->commands, Jim_String(cmdName)); + } + + Jim_AddHashEntry(&interp->commands, Jim_String(cmdName), cmdPtr); + } + + /* Unlike Tcl, set the name of the proc as the result */ + Jim_SetResult(interp, cmdName); + return JIM_OK; + + err: + if (cmdPtr->u.proc.staticVars) { + Jim_FreeHashTable(cmdPtr->u.proc.staticVars); + } + Jim_Free(cmdPtr->u.proc.staticVars); + Jim_DecrRefCount(interp, argListObjPtr); + Jim_DecrRefCount(interp, bodyObjPtr); + Jim_Free(cmdPtr); + return JIM_ERR; +} + +int Jim_DeleteCommand(Jim_Interp *interp, const char *cmdName) +{ + if (Jim_DeleteHashEntry(&interp->commands, cmdName) == JIM_ERR) + return JIM_ERR; + Jim_InterpIncrProcEpoch(interp); + return JIM_OK; +} + +int Jim_RenameCommand(Jim_Interp *interp, const char *oldName, const char *newName) +{ + Jim_HashEntry *he; + + /* Does it exist? */ + he = Jim_FindHashEntry(&interp->commands, oldName); + if (he == NULL) { + Jim_SetResultFormatted(interp, "can't %s \"%s\": command doesn't exist", + newName[0] ? "rename" : "delete", oldName); + return JIM_ERR; + } + + if (newName[0] == '\0') /* Delete! */ + return Jim_DeleteCommand(interp, oldName); + + /* rename */ + if (Jim_FindHashEntry(&interp->commands, newName)) { + Jim_SetResultFormatted(interp, "can't rename to \"%s\": command already exists", newName); + return JIM_ERR; + } + + /* Add the new name first */ + JimIncrCmdRefCount(he->u.val); + Jim_AddHashEntry(&interp->commands, newName, he->u.val); + + /* Now remove the old name */ + Jim_DeleteHashEntry(&interp->commands, oldName); + + /* Increment the epoch */ + Jim_InterpIncrProcEpoch(interp); + return JIM_OK; +} + +/* ----------------------------------------------------------------------------- + * Command object + * ---------------------------------------------------------------------------*/ + +static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); + +static const Jim_ObjType commandObjType = { + "command", + NULL, + NULL, + NULL, + JIM_TYPE_REFERENCES, +}; + +int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr) +{ + Jim_HashEntry *he; + const char *cmdName; + + /* Get the string representation */ + cmdName = Jim_String(objPtr); + /* Lookup this name into the commands hash table */ + he = Jim_FindHashEntry(&interp->commands, cmdName); + if (he == NULL) + return JIM_ERR; + + /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); + objPtr->typePtr = &commandObjType; + objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch; + objPtr->internalRep.cmdValue.cmdPtr = (void *)he->u.val; + return JIM_OK; +} + +/* This function returns the command structure for the command name + * stored in objPtr. It tries to specialize the objPtr to contain + * a cached info instead to perform the lookup into the hash table + * every time. The information cached may not be uptodate, in such + * a case the lookup is performed and the cache updated. + * + * Respects the 'upcall' setting + */ +Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags) +{ + Jim_Cmd *cmd; + + if ((objPtr->typePtr != &commandObjType || + objPtr->internalRep.cmdValue.procEpoch != interp->procEpoch) && + SetCommandFromAny(interp, objPtr) == JIM_ERR) { + if (flags & JIM_ERRMSG) { + Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr); + } + return NULL; + } + cmd = objPtr->internalRep.cmdValue.cmdPtr; + while (cmd->isproc && cmd->u.proc.upcall) { + cmd = cmd->u.proc.prevCmd; + } + return cmd; +} + +/* ----------------------------------------------------------------------------- + * Variables + * ---------------------------------------------------------------------------*/ + +/* Variables HashTable Type. + * + * Keys are dynamic allocated strings, Values are Jim_Var structures. */ +static void JimVariablesHTValDestructor(void *interp, void *val) +{ + Jim_Var *varPtr = (void *)val; + + Jim_DecrRefCount(interp, varPtr->objPtr); + Jim_Free(val); +} + +static const Jim_HashTableType JimVariablesHashTableType = { + JimStringCopyHTHashFunction, /* hash function */ + JimStringCopyHTKeyDup, /* key dup */ + NULL, /* val dup */ + JimStringCopyHTKeyCompare, /* key compare */ + JimStringCopyHTKeyDestructor, /* key destructor */ + JimVariablesHTValDestructor /* val destructor */ +}; + +/* ----------------------------------------------------------------------------- + * Variable object + * ---------------------------------------------------------------------------*/ + +#define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */ + +static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); + +static const Jim_ObjType variableObjType = { + "variable", + NULL, + NULL, + NULL, + JIM_TYPE_REFERENCES, +}; + +/* Return true if the string "str" looks like syntax sugar for [dict]. I.e. + * is in the form "varname(key)". */ +static int JimNameIsDictSugar(const char *str, int len) +{ + if (len && str[len - 1] == ')' && strchr(str, '(') != NULL) + return 1; + return 0; +} + +/** + * Check that the name does not contain embedded nulls. + * + * Variable and procedure names are maniplated as null terminated strings, so + * don't allow names with embedded nulls. + */ +static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr) +{ + /* Variable names and proc names can't contain embedded nulls */ + if (nameObjPtr->typePtr != &variableObjType) { + int len; + const char *str = Jim_GetString(nameObjPtr, &len); + if (memchr(str, '\0', len)) { + Jim_SetResultFormatted(interp, "%s name contains embedded null", type); + return JIM_ERR; + } + } + return JIM_OK; +} + +/* This method should be called only by the variable API. + * It returns JIM_OK on success (variable already exists), + * JIM_ERR if it does not exists, JIM_DICT_SUGAR if it's not + * a variable name, but syntax glue for [dict] i.e. the last + * character is ')' */ +static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) +{ + Jim_HashEntry *he; + const char *varName; + int len; + Jim_CallFrame *framePtr = interp->framePtr; + + /* Check if the object is already an uptodate variable */ + if (objPtr->typePtr == &variableObjType && + objPtr->internalRep.varValue.callFrameId == framePtr->id) { + return JIM_OK; /* nothing to do */ + } + + if (objPtr->typePtr == &dictSubstObjType) { + return JIM_DICT_SUGAR; + } + + if (JimValidName(interp, "variable", objPtr) != JIM_OK) { + return JIM_ERR; + } + + /* Get the string representation */ + varName = Jim_GetString(objPtr, &len); + + /* Make sure it's not syntax glue to get/set dict. */ + if (JimNameIsDictSugar(varName, len)) { + return JIM_DICT_SUGAR; + } + + if (varName[0] == ':' && varName[1] == ':') { + framePtr = interp->topFramePtr; + he = Jim_FindHashEntry(&framePtr->vars, varName + 2); + if (he == NULL) { + return JIM_ERR; + } + } + else { + /* Lookup this name into the variables hash table */ + he = Jim_FindHashEntry(&framePtr->vars, varName); + if (he == NULL) { + /* Try with static vars. */ + if (framePtr->staticVars == NULL) + return JIM_ERR; + if (!(he = Jim_FindHashEntry(framePtr->staticVars, varName))) + return JIM_ERR; + } + } + /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); + objPtr->typePtr = &variableObjType; + objPtr->internalRep.varValue.callFrameId = framePtr->id; + objPtr->internalRep.varValue.varPtr = (void *)he->u.val; + return JIM_OK; +} + +/* -------------------- Variables related functions ------------------------- */ +static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr); +static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags); + +/* For now that's dummy. Variables lookup should be optimized + * in many ways, with caching of lookups, and possibly with + * a table of pre-allocated vars in every CallFrame for local vars. + * All the caching should also have an 'epoch' mechanism similar + * to the one used by Tcl for procedures lookup caching. */ + +int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr) +{ + const char *name; + Jim_Var *var; + int err; + + if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) { + Jim_CallFrame *framePtr = interp->framePtr; + + /* Check for [dict] syntax sugar. */ + if (err == JIM_DICT_SUGAR) + return JimDictSugarSet(interp, nameObjPtr, valObjPtr); + + if (JimValidName(interp, "variable", nameObjPtr) != JIM_OK) { + return JIM_ERR; + } + + /* New variable to create */ + name = Jim_String(nameObjPtr); + + var = Jim_Alloc(sizeof(*var)); + var->objPtr = valObjPtr; + Jim_IncrRefCount(valObjPtr); + var->linkFramePtr = NULL; + /* Insert the new variable */ + if (name[0] == ':' && name[1] == ':') { + /* Into the top level frame */ + framePtr = interp->topFramePtr; + Jim_AddHashEntry(&framePtr->vars, name + 2, var); + } + else { + Jim_AddHashEntry(&framePtr->vars, name, var); + } + /* Make the object int rep a variable */ + Jim_FreeIntRep(interp, nameObjPtr); + nameObjPtr->typePtr = &variableObjType; + nameObjPtr->internalRep.varValue.callFrameId = framePtr->id; + nameObjPtr->internalRep.varValue.varPtr = var; + } + else { + var = nameObjPtr->internalRep.varValue.varPtr; + if (var->linkFramePtr == NULL) { + Jim_IncrRefCount(valObjPtr); + Jim_DecrRefCount(interp, var->objPtr); + var->objPtr = valObjPtr; + } + else { /* Else handle the link */ + Jim_CallFrame *savedCallFrame; + + savedCallFrame = interp->framePtr; + interp->framePtr = var->linkFramePtr; + err = Jim_SetVariable(interp, var->objPtr, valObjPtr); + interp->framePtr = savedCallFrame; + if (err != JIM_OK) + return err; + } + } + return JIM_OK; +} + +int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr) +{ + Jim_Obj *nameObjPtr; + int result; + + nameObjPtr = Jim_NewStringObj(interp, name, -1); + Jim_IncrRefCount(nameObjPtr); + result = Jim_SetVariable(interp, nameObjPtr, objPtr); + Jim_DecrRefCount(interp, nameObjPtr); + return result; +} + +int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr) +{ + Jim_CallFrame *savedFramePtr; + int result; + + savedFramePtr = interp->framePtr; + interp->framePtr = interp->topFramePtr; + result = Jim_SetVariableStr(interp, name, objPtr); + interp->framePtr = savedFramePtr; + return result; +} + +int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val) +{ + Jim_Obj *nameObjPtr, *valObjPtr; + int result; + + nameObjPtr = Jim_NewStringObj(interp, name, -1); + valObjPtr = Jim_NewStringObj(interp, val, -1); + Jim_IncrRefCount(nameObjPtr); + Jim_IncrRefCount(valObjPtr); + result = Jim_SetVariable(interp, nameObjPtr, valObjPtr); + Jim_DecrRefCount(interp, nameObjPtr); + Jim_DecrRefCount(interp, valObjPtr); + return result; +} + +int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr, + Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame) +{ + const char *varName; + int len; + + varName = Jim_GetString(nameObjPtr, &len); + + if (varName[0] == ':' && varName[1] == ':') { + /* Linking a global var does nothing */ + return JIM_OK; + } + + if (JimNameIsDictSugar(varName, len)) { + Jim_SetResultString(interp, "Dict key syntax invalid as link source", -1); + return JIM_ERR; + } + + /* Check for an existing variable or link */ + if (SetVariableFromAny(interp, nameObjPtr) == JIM_OK) { + Jim_Var *varPtr = nameObjPtr->internalRep.varValue.varPtr; + + if (varPtr->linkFramePtr == NULL) { + Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr); + return JIM_ERR; + } + + /* It exists, but is a link, so delete the link */ + varPtr->linkFramePtr = NULL; + } + + /* Check for cycles. */ + if (interp->framePtr == targetCallFrame) { + Jim_Obj *objPtr = targetNameObjPtr; + Jim_Var *varPtr; + + /* Cycles are only possible with 'uplevel 0' */ + while (1) { + if (Jim_StringEqObj(objPtr, nameObjPtr)) { + Jim_SetResultString(interp, "can't upvar from variable to itself", -1); + return JIM_ERR; + } + if (SetVariableFromAny(interp, objPtr) != JIM_OK) + break; + varPtr = objPtr->internalRep.varValue.varPtr; + if (varPtr->linkFramePtr != targetCallFrame) + break; + objPtr = varPtr->objPtr; + } + } + + /* Perform the binding */ + Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr); + /* We are now sure 'nameObjPtr' type is variableObjType */ + nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame; + return JIM_OK; +} + +/* Return the Jim_Obj pointer associated with a variable name, + * or NULL if the variable was not found in the current context. + * The same optimization discussed in the comment to the + * 'SetVariable' function should apply here. + * + * If JIM_UNSHARED is set and the variable is an array element (dict sugar) + * in a dictionary which is shared, the array variable value is duplicated first. + * This allows the array element to be updated (e.g. append, lappend) without + * affecting other references to the dictionary. + */ +Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags) +{ + switch (SetVariableFromAny(interp, nameObjPtr)) { + case JIM_OK:{ + Jim_Var *varPtr = nameObjPtr->internalRep.varValue.varPtr; + + if (varPtr->linkFramePtr == NULL) { + return varPtr->objPtr; + } + else { + Jim_Obj *objPtr; + + /* The variable is a link? Resolve it. */ + Jim_CallFrame *savedCallFrame = interp->framePtr; + + interp->framePtr = varPtr->linkFramePtr; + objPtr = Jim_GetVariable(interp, varPtr->objPtr, flags); + interp->framePtr = savedCallFrame; + if (objPtr) { + return objPtr; + } + /* Error, so fall through to the error message */ + } + } + break; + + case JIM_DICT_SUGAR: + /* [dict] syntax sugar. */ + return JimDictSugarGet(interp, nameObjPtr, flags); + } + if (flags & JIM_ERRMSG) { + Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr); + } + return NULL; +} + +Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags) +{ + Jim_CallFrame *savedFramePtr; + Jim_Obj *objPtr; + + savedFramePtr = interp->framePtr; + interp->framePtr = interp->topFramePtr; + objPtr = Jim_GetVariable(interp, nameObjPtr, flags); + interp->framePtr = savedFramePtr; + + return objPtr; +} + +Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags) +{ + Jim_Obj *nameObjPtr, *varObjPtr; + + nameObjPtr = Jim_NewStringObj(interp, name, -1); + Jim_IncrRefCount(nameObjPtr); + varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags); + Jim_DecrRefCount(interp, nameObjPtr); + return varObjPtr; +} + +Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags) +{ + Jim_CallFrame *savedFramePtr; + Jim_Obj *objPtr; + + savedFramePtr = interp->framePtr; + interp->framePtr = interp->topFramePtr; + objPtr = Jim_GetVariableStr(interp, name, flags); + interp->framePtr = savedFramePtr; + + return objPtr; +} + +/* Unset a variable. + * Note: On success unset invalidates all the variable objects created + * in the current call frame incrementing. */ +int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags) +{ + const char *name; + Jim_Var *varPtr; + int retval; + + retval = SetVariableFromAny(interp, nameObjPtr); + if (retval == JIM_DICT_SUGAR) { + /* [dict] syntax sugar. */ + return JimDictSugarSet(interp, nameObjPtr, NULL); + } + else if (retval == JIM_OK) { + varPtr = nameObjPtr->internalRep.varValue.varPtr; + + /* If it's a link call UnsetVariable recursively */ + if (varPtr->linkFramePtr) { + Jim_CallFrame *savedCallFrame; + + savedCallFrame = interp->framePtr; + interp->framePtr = varPtr->linkFramePtr; + retval = Jim_UnsetVariable(interp, varPtr->objPtr, JIM_NONE); + interp->framePtr = savedCallFrame; + } + else { + Jim_CallFrame *framePtr = interp->framePtr; + + name = Jim_String(nameObjPtr); + if (name[0] == ':' && name[1] == ':') { + framePtr = interp->topFramePtr; + name += 2; + } + retval = Jim_DeleteHashEntry(&framePtr->vars, name); + if (retval == JIM_OK) { + /* Change the callframe id, invalidating var lookup caching */ + JimChangeCallFrameId(interp, framePtr); + } + } + } + if (retval != JIM_OK && (flags & JIM_ERRMSG)) { + Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr); + } + return retval; +} + +/* ---------- Dict syntax sugar (similar to array Tcl syntax) -------------- */ + +/* Given a variable name for [dict] operation syntax sugar, + * this function returns two objects, the first with the name + * of the variable to set, and the second with the rispective key. + * For example "foo(bar)" will return objects with string repr. of + * "foo" and "bar". + * + * The returned objects have refcount = 1. The function can't fail. */ +static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr, + Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr) +{ + const char *str, *p; + int len, keyLen; + Jim_Obj *varObjPtr, *keyObjPtr; + + str = Jim_GetString(objPtr, &len); + + p = strchr(str, '('); + JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str)); + + varObjPtr = Jim_NewStringObj(interp, str, p - str); + + p++; + keyLen = (str + len) - p; + if (str[len - 1] == ')') { + keyLen--; + } + + /* Create the objects with the variable name and key. */ + keyObjPtr = Jim_NewStringObj(interp, p, keyLen); + + Jim_IncrRefCount(varObjPtr); + Jim_IncrRefCount(keyObjPtr); + *varPtrPtr = varObjPtr; + *keyPtrPtr = keyObjPtr; +} + +/* Helper of Jim_SetVariable() to deal with dict-syntax variable names. + * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */ +static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr) +{ + int err; + + SetDictSubstFromAny(interp, objPtr); + + err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, + &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_ERRMSG); + + if (err == JIM_OK) { + /* Don't keep an extra ref to the result */ + Jim_SetEmptyResult(interp); + } + else { + if (!valObjPtr) { + /* Better error message for unset a(2) where a exists but a(2) doesn't */ + if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) { + Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array", + objPtr); + return err; + } + } + /* Make the error more informative and Tcl-compatible */ + Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array", + (valObjPtr ? "set" : "unset"), objPtr); + } + return err; +} + +/** + * Expands the array variable (dict sugar) and returns the result, or NULL on error. + * + * If JIM_UNSHARED is set and the dictionary is shared, it will be duplicated + * and stored back to the variable before expansion. + */ +static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr, + Jim_Obj *keyObjPtr, int flags) +{ + Jim_Obj *dictObjPtr; + Jim_Obj *resObjPtr = NULL; + int ret; + + dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG); + if (!dictObjPtr) { + return NULL; + } + + ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE); + if (ret != JIM_OK) { + resObjPtr = NULL; + if (ret < 0) { + Jim_SetResultFormatted(interp, + "can't read \"%#s(%#s)\": variable isn't array", varObjPtr, keyObjPtr); + } + else { + Jim_SetResultFormatted(interp, + "can't read \"%#s(%#s)\": no such element in array", varObjPtr, keyObjPtr); + } + } + else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) { + dictObjPtr = Jim_DuplicateObj(interp, dictObjPtr); + if (Jim_SetVariable(interp, varObjPtr, dictObjPtr) != JIM_OK) { + /* This can probably never happen */ + JimPanic((1, "SetVariable failed for JIM_UNSHARED")); + } + /* We know that the key exists. Get the result in the now-unshared dictionary */ + Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE); + } + + return resObjPtr; +} + +/* Helper of Jim_GetVariable() to deal with dict-syntax variable names */ +static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags) +{ + SetDictSubstFromAny(interp, objPtr); + + return JimDictExpandArrayVariable(interp, + objPtr->internalRep.dictSubstValue.varNameObjPtr, + objPtr->internalRep.dictSubstValue.indexObjPtr, flags); +} + +/* --------- $var(INDEX) substitution, using a specialized object ----------- */ + +void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) +{ + Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr); + Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr); +} + +void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) +{ + JIM_NOTUSED(interp); + + dupPtr->internalRep.dictSubstValue.varNameObjPtr = + srcPtr->internalRep.dictSubstValue.varNameObjPtr; + dupPtr->internalRep.dictSubstValue.indexObjPtr = srcPtr->internalRep.dictSubstValue.indexObjPtr; + dupPtr->typePtr = &dictSubstObjType; +} + +/* Note: The object *must* be in dict-sugar format */ +static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr) +{ + if (objPtr->typePtr != &dictSubstObjType) { + Jim_Obj *varObjPtr, *keyObjPtr; + + if (objPtr->typePtr == &interpolatedObjType) { + /* An interpolated object in dict-sugar form */ + + const ScriptToken *token = objPtr->internalRep.twoPtrValue.ptr1; + + varObjPtr = token[0].objPtr; + keyObjPtr = objPtr->internalRep.twoPtrValue.ptr2; + + Jim_IncrRefCount(varObjPtr); + Jim_IncrRefCount(keyObjPtr); + } + else { + JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr); + } + + Jim_FreeIntRep(interp, objPtr); + objPtr->typePtr = &dictSubstObjType; + objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr; + objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr; + } +} + +/* This function is used to expand [dict get] sugar in the form + * of $var(INDEX). The function is mainly used by Jim_EvalObj() + * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an + * object that is *guaranteed* to be in the form VARNAME(INDEX). + * The 'index' part is [subst]ituted, and is used to lookup a key inside + * the [dict]ionary contained in variable VARNAME. */ +static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr) +{ + Jim_Obj *resObjPtr = NULL; + Jim_Obj *substKeyObjPtr = NULL; + + SetDictSubstFromAny(interp, objPtr); + + if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr, + &substKeyObjPtr, JIM_NONE) + != JIM_OK) { + return NULL; + } + Jim_IncrRefCount(substKeyObjPtr); + resObjPtr = + JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, + substKeyObjPtr, 0); + Jim_DecrRefCount(interp, substKeyObjPtr); + + return resObjPtr; +} + +static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr) +{ + Jim_Obj *resultObjPtr; + + if (Jim_EvalExpression(interp, objPtr, &resultObjPtr) == JIM_OK) { + /* Note that the result has a ref count of 1, but we need a ref count of 0 */ + resultObjPtr->refCount--; + return resultObjPtr; + } + return NULL; +} + +/* ----------------------------------------------------------------------------- + * CallFrame + * ---------------------------------------------------------------------------*/ + +static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent) +{ + Jim_CallFrame *cf; + + if (interp->freeFramesList) { + cf = interp->freeFramesList; + interp->freeFramesList = cf->nextFramePtr; + } + else { + cf = Jim_Alloc(sizeof(*cf)); + cf->vars.table = NULL; + } + + cf->id = interp->callFrameEpoch++; + cf->parentCallFrame = parent; + cf->level = parent ? parent->level + 1 : 0; + cf->argv = NULL; + cf->argc = 0; + cf->procArgsObjPtr = NULL; + cf->procBodyObjPtr = NULL; + cf->nextFramePtr = NULL; + cf->staticVars = NULL; + if (cf->vars.table == NULL) + Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp); + return cf; +} + +/* Used to invalidate every caching related to callframe stability. */ +static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf) +{ + cf->id = interp->callFrameEpoch++; +} + +#define JIM_FCF_NONE 0 /* no flags */ +#define JIM_FCF_NOHT 1 /* don't free the hash table */ +static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags) +{ + if (cf->procArgsObjPtr) + Jim_DecrRefCount(interp, cf->procArgsObjPtr); + if (cf->procBodyObjPtr) + Jim_DecrRefCount(interp, cf->procBodyObjPtr); + if (!(flags & JIM_FCF_NOHT)) + Jim_FreeHashTable(&cf->vars); + else { + int i; + Jim_HashEntry **table = cf->vars.table, *he; + + for (i = 0; i < JIM_HT_INITIAL_SIZE; i++) { + he = table[i]; + while (he != NULL) { + Jim_HashEntry *nextEntry = he->next; + Jim_Var *varPtr = (void *)he->u.val; + + Jim_DecrRefCount(interp, varPtr->objPtr); + Jim_Free(he->u.val); + Jim_Free((void *)he->key); /* ATTENTION: const cast */ + Jim_Free(he); + table[i] = NULL; + he = nextEntry; + } + } + cf->vars.used = 0; + } + cf->nextFramePtr = interp->freeFramesList; + interp->freeFramesList = cf; +} + +/* ----------------------------------------------------------------------------- + * References + * ---------------------------------------------------------------------------*/ +#ifdef JIM_REFERENCES + +/* References HashTable Type. + * + * Keys are jim_wide integers, dynamically allocated for now but in the + * future it's worth to cache this 8 bytes objects. Values are poitners + * to Jim_References. */ +static void JimReferencesHTValDestructor(void *interp, void *val) +{ + Jim_Reference *refPtr = (void *)val; + + Jim_DecrRefCount(interp, refPtr->objPtr); + if (refPtr->finalizerCmdNamePtr != NULL) { + Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr); + } + Jim_Free(val); +} + +static unsigned int JimReferencesHTHashFunction(const void *key) +{ + /* Only the least significant bits are used. */ + const jim_wide *widePtr = key; + unsigned int intValue = (unsigned int)*widePtr; + + return Jim_IntHashFunction(intValue); +} + +static const void *JimReferencesHTKeyDup(void *privdata, const void *key) +{ + void *copy = Jim_Alloc(sizeof(jim_wide)); + + JIM_NOTUSED(privdata); + + memcpy(copy, key, sizeof(jim_wide)); + return copy; +} + +static int JimReferencesHTKeyCompare(void *privdata, const void *key1, const void *key2) +{ + JIM_NOTUSED(privdata); + + return memcmp(key1, key2, sizeof(jim_wide)) == 0; +} + +static void JimReferencesHTKeyDestructor(void *privdata, const void *key) +{ + JIM_NOTUSED(privdata); + + Jim_Free((void *)key); +} + +static const Jim_HashTableType JimReferencesHashTableType = { + JimReferencesHTHashFunction, /* hash function */ + JimReferencesHTKeyDup, /* key dup */ + NULL, /* val dup */ + JimReferencesHTKeyCompare, /* key compare */ + JimReferencesHTKeyDestructor, /* key destructor */ + JimReferencesHTValDestructor /* val destructor */ +}; + +/* ----------------------------------------------------------------------------- + * Reference object type and References API + * ---------------------------------------------------------------------------*/ + +/* The string representation of references has two features in order + * to make the GC faster. The first is that every reference starts + * with a non common character '<', in order to make the string matching + * faster. The second is that the reference string rep is 42 characters + * in length, this allows to avoid to check every object with a string + * repr < 42, and usually there aren't many of these objects. */ + +#define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN) + +static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id) +{ + const char *fmt = ".%020" JIM_WIDE_MODIFIER ">"; + + sprintf(buf, fmt, refPtr->tag, id); + return JIM_REFERENCE_SPACE; +} + +static void UpdateStringOfReference(struct Jim_Obj *objPtr); + +static const Jim_ObjType referenceObjType = { + "reference", + NULL, + NULL, + UpdateStringOfReference, + JIM_TYPE_REFERENCES, +}; + +void UpdateStringOfReference(struct Jim_Obj *objPtr) +{ + int len; + char buf[JIM_REFERENCE_SPACE + 1]; + Jim_Reference *refPtr; + + refPtr = objPtr->internalRep.refValue.refPtr; + len = JimFormatReference(buf, refPtr, objPtr->internalRep.refValue.id); + objPtr->bytes = Jim_Alloc(len + 1); + memcpy(objPtr->bytes, buf, len + 1); + objPtr->length = len; +} + +/* returns true if 'c' is a valid reference tag character. + * i.e. inside the range [_a-zA-Z0-9] */ +static int isrefchar(int c) +{ + return (c == '_' || isalnum(c)); +} + +static int SetReferenceFromAny(Jim_Interp *interp, Jim_Obj *objPtr) +{ + jim_wide wideValue; + int i, len; + const char *str, *start, *end; + char refId[21]; + Jim_Reference *refPtr; + Jim_HashEntry *he; + + /* Get the string representation */ + str = Jim_GetString(objPtr, &len); + /* Check if it looks like a reference */ + if (len < JIM_REFERENCE_SPACE) + goto badformat; + /* Trim spaces */ + start = str; + end = str + len - 1; + while (*start == ' ') + start++; + while (*end == ' ' && end > start) + end--; + if (end - start + 1 != JIM_REFERENCE_SPACE) + goto badformat; + /* .%020> */ + if (memcmp(start, "references, &wideValue); + if (he == NULL) { + Jim_SetResultFormatted(interp, "invalid reference id \"%#s\"", objPtr); + return JIM_ERR; + } + refPtr = he->u.val; + /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); + objPtr->typePtr = &referenceObjType; + objPtr->internalRep.refValue.id = wideValue; + objPtr->internalRep.refValue.refPtr = refPtr; + return JIM_OK; + + badformat: + Jim_SetResultFormatted(interp, "expected reference but got \"%#s\"", objPtr); + return JIM_ERR; +} + +/* Returns a new reference pointing to objPtr, having cmdNamePtr + * as finalizer command (or NULL if there is no finalizer). + * The returned reference object has refcount = 0. */ +Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr) +{ + struct Jim_Reference *refPtr; + jim_wide wideValue = interp->referenceNextId; + Jim_Obj *refObjPtr; + const char *tag; + int tagLen, i; + + /* Perform the Garbage Collection if needed. */ + Jim_CollectIfNeeded(interp); + + refPtr = Jim_Alloc(sizeof(*refPtr)); + refPtr->objPtr = objPtr; + Jim_IncrRefCount(objPtr); + refPtr->finalizerCmdNamePtr = cmdNamePtr; + if (cmdNamePtr) + Jim_IncrRefCount(cmdNamePtr); + Jim_AddHashEntry(&interp->references, &wideValue, refPtr); + refObjPtr = Jim_NewObj(interp); + refObjPtr->typePtr = &referenceObjType; + refObjPtr->bytes = NULL; + refObjPtr->internalRep.refValue.id = interp->referenceNextId; + refObjPtr->internalRep.refValue.refPtr = refPtr; + interp->referenceNextId++; + /* Set the tag. Trimmed at JIM_REFERENCE_TAGLEN. Everything + * that does not pass the 'isrefchar' test is replaced with '_' */ + tag = Jim_GetString(tagPtr, &tagLen); + if (tagLen > JIM_REFERENCE_TAGLEN) + tagLen = JIM_REFERENCE_TAGLEN; + for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) { + if (i < tagLen && isrefchar(tag[i])) + refPtr->tag[i] = tag[i]; + else + refPtr->tag[i] = '_'; + } + refPtr->tag[JIM_REFERENCE_TAGLEN] = '\0'; + return refObjPtr; +} + +Jim_Reference *Jim_GetReference(Jim_Interp *interp, Jim_Obj *objPtr) +{ + if (objPtr->typePtr != &referenceObjType && SetReferenceFromAny(interp, objPtr) == JIM_ERR) + return NULL; + return objPtr->internalRep.refValue.refPtr; +} + +int Jim_SetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr) +{ + Jim_Reference *refPtr; + + if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL) + return JIM_ERR; + Jim_IncrRefCount(cmdNamePtr); + if (refPtr->finalizerCmdNamePtr) + Jim_DecrRefCount(interp, refPtr->finalizerCmdNamePtr); + refPtr->finalizerCmdNamePtr = cmdNamePtr; + return JIM_OK; +} + +int Jim_GetFinalizer(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr) +{ + Jim_Reference *refPtr; + + if ((refPtr = Jim_GetReference(interp, objPtr)) == NULL) + return JIM_ERR; + *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr; + return JIM_OK; +} + +/* ----------------------------------------------------------------------------- + * References Garbage Collection + * ---------------------------------------------------------------------------*/ + +/* This the hash table type for the "MARK" phase of the GC */ +static const Jim_HashTableType JimRefMarkHashTableType = { + JimReferencesHTHashFunction, /* hash function */ + JimReferencesHTKeyDup, /* key dup */ + NULL, /* val dup */ + JimReferencesHTKeyCompare, /* key compare */ + JimReferencesHTKeyDestructor, /* key destructor */ + NULL /* val destructor */ +}; + +/* Performs the garbage collection. */ +int Jim_Collect(Jim_Interp *interp) +{ + int collected = 0; +#ifndef JIM_BOOTSTRAP + Jim_HashTable marks; + Jim_HashTableIterator *htiter; + Jim_HashEntry *he; + Jim_Obj *objPtr; + + /* Avoid recursive calls */ + if (interp->lastCollectId == -1) { + /* Jim_Collect() already running. Return just now. */ + return 0; + } + interp->lastCollectId = -1; + + /* Mark all the references found into the 'mark' hash table. + * The references are searched in every live object that + * is of a type that can contain references. */ + Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL); + objPtr = interp->liveList; + while (objPtr) { + if (objPtr->typePtr == NULL || objPtr->typePtr->flags & JIM_TYPE_REFERENCES) { + const char *str, *p; + int len; + + /* If the object is of type reference, to get the + * Id is simple... */ + if (objPtr->typePtr == &referenceObjType) { + Jim_AddHashEntry(&marks, &objPtr->internalRep.refValue.id, NULL); +#ifdef JIM_DEBUG_GC + printf("MARK (reference): %d refcount: %d" JIM_NL, + (int)objPtr->internalRep.refValue.id, objPtr->refCount); +#endif + objPtr = objPtr->nextObjPtr; + continue; + } + /* Get the string repr of the object we want + * to scan for references. */ + p = str = Jim_GetString(objPtr, &len); + /* Skip objects too little to contain references. */ + if (len < JIM_REFERENCE_SPACE) { + objPtr = objPtr->nextObjPtr; + continue; + } + /* Extract references from the object string repr. */ + while (1) { + int i; + jim_wide id; + char buf[21]; + + if ((p = strstr(p, "nextObjPtr; + } + + /* Run the references hash table to destroy every reference that + * is not referenced outside (not present in the mark HT). */ + htiter = Jim_GetHashTableIterator(&interp->references); + while ((he = Jim_NextHashEntry(htiter)) != NULL) { + const jim_wide *refId; + Jim_Reference *refPtr; + + refId = he->key; + /* Check if in the mark phase we encountered + * this reference. */ + if (Jim_FindHashEntry(&marks, refId) == NULL) { +#ifdef JIM_DEBUG_GC + printf("COLLECTING %d" JIM_NL, (int)*refId); +#endif + collected++; + /* Drop the reference, but call the + * finalizer first if registered. */ + refPtr = he->u.val; + if (refPtr->finalizerCmdNamePtr) { + char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE + 1); + Jim_Obj *objv[3], *oldResult; + + JimFormatReference(refstr, refPtr, *refId); + + objv[0] = refPtr->finalizerCmdNamePtr; + objv[1] = Jim_NewStringObjNoAlloc(interp, refstr, 32); + objv[2] = refPtr->objPtr; + Jim_IncrRefCount(objv[0]); + Jim_IncrRefCount(objv[1]); + Jim_IncrRefCount(objv[2]); + + /* Drop the reference itself */ + Jim_DeleteHashEntry(&interp->references, refId); + + /* Call the finalizer. Errors ignored. */ + oldResult = interp->result; + Jim_IncrRefCount(oldResult); + Jim_EvalObjVector(interp, 3, objv); + Jim_SetResult(interp, oldResult); + Jim_DecrRefCount(interp, oldResult); + + Jim_DecrRefCount(interp, objv[0]); + Jim_DecrRefCount(interp, objv[1]); + Jim_DecrRefCount(interp, objv[2]); + } + else { + Jim_DeleteHashEntry(&interp->references, refId); + } + } + } + Jim_FreeHashTableIterator(htiter); + Jim_FreeHashTable(&marks); + interp->lastCollectId = interp->referenceNextId; + interp->lastCollectTime = time(NULL); +#endif /* JIM_BOOTSTRAP */ + return collected; +} + +#define JIM_COLLECT_ID_PERIOD 5000 +#define JIM_COLLECT_TIME_PERIOD 300 + +void Jim_CollectIfNeeded(Jim_Interp *interp) +{ + jim_wide elapsedId; + int elapsedTime; + + elapsedId = interp->referenceNextId - interp->lastCollectId; + elapsedTime = time(NULL) - interp->lastCollectTime; + + + if (elapsedId > JIM_COLLECT_ID_PERIOD || elapsedTime > JIM_COLLECT_TIME_PERIOD) { + Jim_Collect(interp); + } +} +#endif + +static int JimIsBigEndian(void) +{ + union { + unsigned short s; + unsigned char c[2]; + } uval = {0x0102}; + + return uval.c[0] == 1; +} + +/* ----------------------------------------------------------------------------- + * Interpreter related functions + * ---------------------------------------------------------------------------*/ + +Jim_Interp *Jim_CreateInterp(void) +{ + Jim_Interp *i = Jim_Alloc(sizeof(*i)); + + memset(i, 0, sizeof(*i)); + + i->maxNestingDepth = JIM_MAX_NESTING_DEPTH; + i->lastCollectTime = time(NULL); + + /* Note that we can create objects only after the + * interpreter liveList and freeList pointers are + * initialized to NULL. */ + Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i); +#ifdef JIM_REFERENCES + Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i); +#endif + Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i); + Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL); + i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL); + i->emptyObj = Jim_NewEmptyStringObj(i); + i->trueObj = Jim_NewIntObj(i, 1); + i->falseObj = Jim_NewIntObj(i, 0); + i->errorFileNameObj = i->emptyObj; + i->result = i->emptyObj; + i->stackTrace = Jim_NewListObj(i, NULL, 0); + i->unknown = Jim_NewStringObj(i, "unknown", -1); + i->errorProc = i->emptyObj; + i->currentScriptObj = Jim_NewEmptyStringObj(i); + Jim_IncrRefCount(i->emptyObj); + Jim_IncrRefCount(i->errorFileNameObj); + Jim_IncrRefCount(i->result); + Jim_IncrRefCount(i->stackTrace); + Jim_IncrRefCount(i->unknown); + Jim_IncrRefCount(i->currentScriptObj); + Jim_IncrRefCount(i->errorProc); + Jim_IncrRefCount(i->trueObj); + Jim_IncrRefCount(i->falseObj); + + /* Initialize key variables every interpreter should contain */ + Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY); + Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0"); + + Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS); + Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM); + Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR); + Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", JimIsBigEndian() ? "bigEndian" : "littleEndian"); + Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0"); + Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *))); + Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide))); + + return i; +} + +void Jim_FreeInterp(Jim_Interp *i) +{ + Jim_CallFrame *cf = i->framePtr, *prevcf, *nextcf; + Jim_Obj *objPtr, *nextObjPtr; + + Jim_DecrRefCount(i, i->emptyObj); + Jim_DecrRefCount(i, i->trueObj); + Jim_DecrRefCount(i, i->falseObj); + Jim_DecrRefCount(i, i->result); + Jim_DecrRefCount(i, i->stackTrace); + Jim_DecrRefCount(i, i->errorProc); + Jim_DecrRefCount(i, i->unknown); + Jim_DecrRefCount(i, i->errorFileNameObj); + Jim_DecrRefCount(i, i->currentScriptObj); + Jim_FreeHashTable(&i->commands); +#ifdef JIM_REFERENCES + Jim_FreeHashTable(&i->references); +#endif + Jim_FreeHashTable(&i->packages); + Jim_Free(i->prngState); + Jim_FreeHashTable(&i->assocData); + JimDeleteLocalProcs(i); + + /* Free the call frames list */ + while (cf) { + prevcf = cf->parentCallFrame; + JimFreeCallFrame(i, cf, JIM_FCF_NONE); + cf = prevcf; + } + /* Check that the live object list is empty, otherwise + * there is a memory leak. */ + if (i->liveList != NULL) { + objPtr = i->liveList; + + printf(JIM_NL "-------------------------------------" JIM_NL); + printf("Objects still in the free list:" JIM_NL); + while (objPtr) { + const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string"; + + printf("%p (%d) %-10s: '%.20s'" JIM_NL, + (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)"); + if (objPtr->typePtr == &sourceObjType) { + printf("FILE %s LINE %d" JIM_NL, + Jim_String(objPtr->internalRep.sourceValue.fileNameObj), + objPtr->internalRep.sourceValue.lineNumber); + } + objPtr = objPtr->nextObjPtr; + } + printf("-------------------------------------" JIM_NL JIM_NL); + JimPanic((1, "Live list non empty freeing the interpreter! Leak?")); + } + /* Free all the freed objects. */ + objPtr = i->freeList; + while (objPtr) { + nextObjPtr = objPtr->nextObjPtr; + Jim_Free(objPtr); + objPtr = nextObjPtr; + } + /* Free cached CallFrame structures */ + cf = i->freeFramesList; + while (cf) { + nextcf = cf->nextFramePtr; + if (cf->vars.table != NULL) + Jim_Free(cf->vars.table); + Jim_Free(cf); + cf = nextcf; + } +#ifdef jim_ext_load + Jim_FreeLoadHandles(i); +#endif + + /* Free the interpreter structure. */ + Jim_Free(i); +} + +/* Returns the call frame relative to the level represented by + * levelObjPtr. If levelObjPtr == NULL, the * level is assumed to be '1'. + * + * This function accepts the 'level' argument in the form + * of the commands [uplevel] and [upvar]. + * + * For a function accepting a relative integer as level suitable + * for implementation of [info level ?level?] check the + * JimGetCallFrameByInteger() function. + * + * Returns NULL on error. + */ +Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr) +{ + long level; + const char *str; + Jim_CallFrame *framePtr; + + if (levelObjPtr) { + str = Jim_String(levelObjPtr); + if (str[0] == '#') { + char *endptr; + + level = strtol(str + 1, &endptr, 0); + if (str[1] == '\0' || endptr[0] != '\0') { + level = -1; + } + } + else { + if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) { + level = -1; + } + else { + /* Convert from a relative to an absolute level */ + level = interp->framePtr->level - level; + } + } + } + else { + str = "1"; /* Needed to format the error message. */ + level = interp->framePtr->level - 1; + } + + if (level == 0) { + return interp->topFramePtr; + } + if (level > 0) { + /* Lookup */ + for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parentCallFrame) { + if (framePtr->level == level) { + return framePtr; + } + } + } + + Jim_SetResultFormatted(interp, "bad level \"%s\"", str); + return NULL; +} + +/* Similar to Jim_GetCallFrameByLevel() but the level is specified + * as a relative integer like in the [info level ?level?] command. + **/ +static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr) +{ + long level; + Jim_CallFrame *framePtr; + + if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) { + if (level <= 0) { + /* Convert from a relative to an absolute level */ + level = interp->framePtr->level + level; + } + + if (level == 0) { + return interp->topFramePtr; + } + + /* Lookup */ + for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parentCallFrame) { + if (framePtr->level == level) { + return framePtr; + } + } + } + + Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr); + return NULL; +} + +static void JimResetStackTrace(Jim_Interp *interp) +{ + Jim_DecrRefCount(interp, interp->stackTrace); + interp->stackTrace = Jim_NewListObj(interp, NULL, 0); + Jim_IncrRefCount(interp->stackTrace); +} + +static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj) +{ + int len; + + /* Increment reference first in case these are the same object */ + Jim_IncrRefCount(stackTraceObj); + Jim_DecrRefCount(interp, interp->stackTrace); + interp->stackTrace = stackTraceObj; + interp->errorFlag = 1; + + /* This is a bit ugly. + * If the filename of the last entry of the stack trace is empty, + * the next stack level should be added. + */ + len = Jim_ListLength(interp, interp->stackTrace); + if (len >= 3) { + Jim_Obj *filenameObj; + + Jim_ListIndex(interp, interp->stackTrace, len - 2, &filenameObj, JIM_NONE); + + Jim_GetString(filenameObj, &len); + + if (!Jim_Length(filenameObj)) { + interp->addStackTrace = 1; + } + } +} + +/* Returns 1 if the stack trace information was used or 0 if not */ +static void JimAppendStackTrace(Jim_Interp *interp, const char *procname, + Jim_Obj *fileNameObj, int linenr) +{ + if (strcmp(procname, "unknown") == 0) { + procname = ""; + } + if (!*procname && !Jim_Length(fileNameObj)) { + /* No useful info here */ + return; + } + + if (Jim_IsShared(interp->stackTrace)) { + Jim_DecrRefCount(interp, interp->stackTrace); + interp->stackTrace = Jim_DuplicateObj(interp, interp->stackTrace); + Jim_IncrRefCount(interp->stackTrace); + } + + /* If we have no procname but the previous element did, merge with that frame */ + if (!*procname && Jim_Length(fileNameObj)) { + /* Just a filename. Check the previous entry */ + int len = Jim_ListLength(interp, interp->stackTrace); + + if (len >= 3) { + Jim_Obj *objPtr; + if (Jim_ListIndex(interp, interp->stackTrace, len - 3, &objPtr, JIM_NONE) == JIM_OK && Jim_Length(objPtr)) { + /* Yes, the previous level had procname */ + if (Jim_ListIndex(interp, interp->stackTrace, len - 2, &objPtr, JIM_NONE) == JIM_OK && !Jim_Length(objPtr)) { + /* But no filename, so merge the new info with that frame */ + ListSetIndex(interp, interp->stackTrace, len - 2, fileNameObj, 0); + ListSetIndex(interp, interp->stackTrace, len - 1, Jim_NewIntObj(interp, linenr), 0); + return; + } + } + } + } + + Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewStringObj(interp, procname, -1)); + Jim_ListAppendElement(interp, interp->stackTrace, fileNameObj); + Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewIntObj(interp, linenr)); +} + +int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc, + void *data) +{ + AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue)); + + assocEntryPtr->delProc = delProc; + assocEntryPtr->data = data; + return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr); +} + +void *Jim_GetAssocData(Jim_Interp *interp, const char *key) +{ + Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key); + + if (entryPtr != NULL) { + AssocDataValue *assocEntryPtr = (AssocDataValue *) entryPtr->u.val; + + return assocEntryPtr->data; + } + return NULL; +} + +int Jim_DeleteAssocData(Jim_Interp *interp, const char *key) +{ + return Jim_DeleteHashEntry(&interp->assocData, key); +} + +int Jim_GetExitCode(Jim_Interp *interp) +{ + return interp->exitCode; +} + +/* ----------------------------------------------------------------------------- + * Integer object + * ---------------------------------------------------------------------------*/ +#define JIM_INTEGER_SPACE 24 + +static void UpdateStringOfInt(struct Jim_Obj *objPtr); +static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags); + +static const Jim_ObjType intObjType = { + "int", + NULL, + NULL, + UpdateStringOfInt, + JIM_TYPE_NONE, +}; + +/* A coerced double is closer to an int than a double. + * It is an int value temporarily masquerading as a double value. + * i.e. it has the same string value as an int and Jim_GetWide() + * succeeds, but also Jim_GetDouble() returns the value directly. + */ +static const Jim_ObjType coercedDoubleObjType = { + "coerced-double", + NULL, + NULL, + UpdateStringOfInt, + JIM_TYPE_NONE, +}; + + +void UpdateStringOfInt(struct Jim_Obj *objPtr) +{ + int len; + char buf[JIM_INTEGER_SPACE + 1]; + + len = Jim_WideToString(buf, JimWideValue(objPtr)); + objPtr->bytes = Jim_Alloc(len + 1); + memcpy(objPtr->bytes, buf, len + 1); + objPtr->length = len; +} + +int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags) +{ + jim_wide wideValue; + const char *str; + + if (objPtr->typePtr == &coercedDoubleObjType) { + /* Simple switcheroo */ + objPtr->typePtr = &intObjType; + return JIM_OK; + } + + /* Get the string representation */ + str = Jim_String(objPtr); + /* Try to convert into a jim_wide */ + if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) { + if (flags & JIM_ERRMSG) { + Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr); + } + return JIM_ERR; + } + if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) { + Jim_SetResultString(interp, "Integer value too big to be represented", -1); + return JIM_ERR; + } + /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); + objPtr->typePtr = &intObjType; + objPtr->internalRep.wideValue = wideValue; + return JIM_OK; +} + +#ifdef JIM_OPTIMIZATION +static int JimIsWide(Jim_Obj *objPtr) +{ + return objPtr->typePtr == &intObjType; +} +#endif + +int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr) +{ + if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR) + return JIM_ERR; + *widePtr = JimWideValue(objPtr); + return JIM_OK; +} + +/* Get a wide but does not set an error if the format is bad. */ +static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr) +{ + if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR) + return JIM_ERR; + *widePtr = JimWideValue(objPtr); + return JIM_OK; +} + +int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr) +{ + jim_wide wideValue; + int retval; + + retval = Jim_GetWide(interp, objPtr, &wideValue); + if (retval == JIM_OK) { + *longPtr = (long)wideValue; + return JIM_OK; + } + return JIM_ERR; +} + +Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue) +{ + Jim_Obj *objPtr; + + objPtr = Jim_NewObj(interp); + objPtr->typePtr = &intObjType; + objPtr->bytes = NULL; + objPtr->internalRep.wideValue = wideValue; + return objPtr; +} + +/* ----------------------------------------------------------------------------- + * Double object + * ---------------------------------------------------------------------------*/ +#define JIM_DOUBLE_SPACE 30 + +static void UpdateStringOfDouble(struct Jim_Obj *objPtr); +static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr); + +static const Jim_ObjType doubleObjType = { + "double", + NULL, + NULL, + UpdateStringOfDouble, + JIM_TYPE_NONE, +}; + +void UpdateStringOfDouble(struct Jim_Obj *objPtr) +{ + int len; + char buf[JIM_DOUBLE_SPACE + 1]; + + len = Jim_DoubleToString(buf, objPtr->internalRep.doubleValue); + objPtr->bytes = Jim_Alloc(len + 1); + memcpy(objPtr->bytes, buf, len + 1); + objPtr->length = len; +} + +int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr) +{ + double doubleValue; + jim_wide wideValue; + const char *str; + + /* Preserve the string representation. + * Needed so we can convert back to int without loss + */ + str = Jim_String(objPtr); + +#ifdef HAVE_LONG_LONG + /* Assume a 53 bit mantissa */ +#define MIN_INT_IN_DOUBLE -(1LL << 53) +#define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1) + + if (objPtr->typePtr == &intObjType + && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE + && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) { + + /* Direct conversion to coerced double */ + objPtr->typePtr = &coercedDoubleObjType; + return JIM_OK; + } + else +#endif + if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) { + /* Managed to convert to an int, so we can use this as a cooerced double */ + Jim_FreeIntRep(interp, objPtr); + objPtr->typePtr = &coercedDoubleObjType; + objPtr->internalRep.wideValue = wideValue; + return JIM_OK; + } + else { + /* Try to convert into a double */ + if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) { + Jim_SetResultFormatted(interp, "expected number but got \"%#s\"", objPtr); + return JIM_ERR; + } + /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); + } + objPtr->typePtr = &doubleObjType; + objPtr->internalRep.doubleValue = doubleValue; + return JIM_OK; +} + +int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr) +{ + if (objPtr->typePtr == &coercedDoubleObjType) { + *doublePtr = JimWideValue(objPtr); + return JIM_OK; + } + if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR) + return JIM_ERR; + + if (objPtr->typePtr == &coercedDoubleObjType) { + *doublePtr = JimWideValue(objPtr); + } + else { + *doublePtr = objPtr->internalRep.doubleValue; + } + return JIM_OK; +} + +Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue) +{ + Jim_Obj *objPtr; + + objPtr = Jim_NewObj(interp); + objPtr->typePtr = &doubleObjType; + objPtr->bytes = NULL; + objPtr->internalRep.doubleValue = doubleValue; + return objPtr; +} + +/* ----------------------------------------------------------------------------- + * List object + * ---------------------------------------------------------------------------*/ +static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr); +static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); +static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); +static void UpdateStringOfList(struct Jim_Obj *objPtr); +static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); + +/* Note that while the elements of the list may contain references, + * the list object itself can't. This basically means that the + * list object string representation as a whole can't contain references + * that are not presents in the single elements. */ +static const Jim_ObjType listObjType = { + "list", + FreeListInternalRep, + DupListInternalRep, + UpdateStringOfList, + JIM_TYPE_NONE, +}; + +void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) +{ + int i; + + for (i = 0; i < objPtr->internalRep.listValue.len; i++) { + Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]); + } + Jim_Free(objPtr->internalRep.listValue.ele); +} + +void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) +{ + int i; + + JIM_NOTUSED(interp); + + dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len; + dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen; + dupPtr->internalRep.listValue.ele = + Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen); + memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele, + sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len); + for (i = 0; i < dupPtr->internalRep.listValue.len; i++) { + Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]); + } + dupPtr->typePtr = &listObjType; +} + +/* The following function checks if a given string can be encoded + * into a list element without any kind of quoting, surrounded by braces, + * or using escapes to quote. */ +#define JIM_ELESTR_SIMPLE 0 +#define JIM_ELESTR_BRACE 1 +#define JIM_ELESTR_QUOTE 2 +static int ListElementQuotingType(const char *s, int len) +{ + int i, level, blevel, trySimple = 1; + + /* Try with the SIMPLE case */ + if (len == 0) + return JIM_ELESTR_BRACE; + if (s[0] == '#') + return JIM_ELESTR_BRACE; + if (s[0] == '"' || s[0] == '{') { + trySimple = 0; + goto testbrace; + } + for (i = 0; i < len; i++) { + switch (s[i]) { + case ' ': + case '$': + case '"': + case '[': + case ']': + case ';': + case '\\': + case '\r': + case '\n': + case '\t': + case '\f': + case '\v': + trySimple = 0; + case '{': + case '}': + goto testbrace; + } + } + return JIM_ELESTR_SIMPLE; + + testbrace: + /* Test if it's possible to do with braces */ + if (s[len - 1] == '\\') + return JIM_ELESTR_QUOTE; + level = 0; + blevel = 0; + for (i = 0; i < len; i++) { + switch (s[i]) { + case '{': + level++; + break; + case '}': + level--; + if (level < 0) + return JIM_ELESTR_QUOTE; + break; + case '[': + blevel++; + break; + case ']': + blevel--; + break; + case '\\': + if (s[i + 1] == '\n') + return JIM_ELESTR_QUOTE; + else if (s[i + 1] != '\0') + i++; + break; + } + } + if (blevel < 0) { + return JIM_ELESTR_QUOTE; + } + + if (level == 0) { + if (!trySimple) + return JIM_ELESTR_BRACE; + for (i = 0; i < len; i++) { + switch (s[i]) { + case ' ': + case '$': + case '"': + case '[': + case ']': + case ';': + case '\\': + case '\r': + case '\n': + case '\t': + case '\f': + case '\v': + return JIM_ELESTR_BRACE; + break; + } + } + return JIM_ELESTR_SIMPLE; + } + return JIM_ELESTR_QUOTE; +} + +/* Returns the malloc-ed representation of a string + * using backslash to quote special chars. */ +static char *BackslashQuoteString(const char *s, int len, int *qlenPtr) +{ + char *q = Jim_Alloc(len * 2 + 1), *p; + + p = q; + while (*s) { + switch (*s) { + case ' ': + case '$': + case '"': + case '[': + case ']': + case '{': + case '}': + case ';': + case '\\': + *p++ = '\\'; + *p++ = *s++; + break; + case '\n': + *p++ = '\\'; + *p++ = 'n'; + s++; + break; + case '\r': + *p++ = '\\'; + *p++ = 'r'; + s++; + break; + case '\t': + *p++ = '\\'; + *p++ = 't'; + s++; + break; + case '\f': + *p++ = '\\'; + *p++ = 'f'; + s++; + break; + case '\v': + *p++ = '\\'; + *p++ = 'v'; + s++; + break; + default: + *p++ = *s++; + break; + } + } + *p = '\0'; + *qlenPtr = p - q; + return q; +} + +static void UpdateStringOfList(struct Jim_Obj *objPtr) +{ + int i, bufLen, realLength; + const char *strRep; + char *p; + int *quotingType; + Jim_Obj **ele = objPtr->internalRep.listValue.ele; + + /* (Over) Estimate the space needed. */ + quotingType = Jim_Alloc(sizeof(int) * objPtr->internalRep.listValue.len + 1); + bufLen = 0; + for (i = 0; i < objPtr->internalRep.listValue.len; i++) { + int len; + + strRep = Jim_GetString(ele[i], &len); + quotingType[i] = ListElementQuotingType(strRep, len); + switch (quotingType[i]) { + case JIM_ELESTR_SIMPLE: + bufLen += len; + break; + case JIM_ELESTR_BRACE: + bufLen += len + 2; + break; + case JIM_ELESTR_QUOTE: + bufLen += len * 2; + break; + } + bufLen++; /* elements separator. */ + } + bufLen++; + + /* Generate the string rep. */ + p = objPtr->bytes = Jim_Alloc(bufLen + 1); + realLength = 0; + for (i = 0; i < objPtr->internalRep.listValue.len; i++) { + int len, qlen; + char *q; + + strRep = Jim_GetString(ele[i], &len); + + switch (quotingType[i]) { + case JIM_ELESTR_SIMPLE: + memcpy(p, strRep, len); + p += len; + realLength += len; + break; + case JIM_ELESTR_BRACE: + *p++ = '{'; + memcpy(p, strRep, len); + p += len; + *p++ = '}'; + realLength += len + 2; + break; + case JIM_ELESTR_QUOTE: + q = BackslashQuoteString(strRep, len, &qlen); + memcpy(p, q, qlen); + Jim_Free(q); + p += qlen; + realLength += qlen; + break; + } + /* Add a separating space */ + if (i + 1 != objPtr->internalRep.listValue.len) { + *p++ = ' '; + realLength++; + } + } + *p = '\0'; /* nul term. */ + objPtr->length = realLength; + Jim_Free(quotingType); +} + +int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) +{ + struct JimParserCtx parser; + const char *str; + int strLen; + Jim_Obj *fileNameObj; + int linenr; + + /* Try to preserve information about filename / line number */ + if (objPtr->typePtr == &sourceObjType) { + fileNameObj = objPtr->internalRep.sourceValue.fileNameObj; + linenr = objPtr->internalRep.sourceValue.lineNumber; + } + else { + fileNameObj = interp->emptyObj; + linenr = 1; + } + Jim_IncrRefCount(fileNameObj); + + /* Get the string representation */ + str = Jim_GetString(objPtr, &strLen); + + /* Free the old internal repr just now and initialize the + * new one just now. The string->list conversion can't fail. */ + Jim_FreeIntRep(interp, objPtr); + objPtr->typePtr = &listObjType; + objPtr->internalRep.listValue.len = 0; + objPtr->internalRep.listValue.maxLen = 0; + objPtr->internalRep.listValue.ele = NULL; + + /* Convert into a list */ + JimParserInit(&parser, str, strLen, linenr); + while (!parser.eof) { + Jim_Obj *elementPtr; + + JimParseList(&parser); + if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC) + continue; + elementPtr = JimParserGetTokenObj(interp, &parser); + JimSetSourceInfo(interp, elementPtr, fileNameObj, parser.tline); + ListAppendElement(objPtr, elementPtr); + } + Jim_DecrRefCount(interp, fileNameObj); + return JIM_OK; +} + +Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len) +{ + Jim_Obj *objPtr; + int i; + + objPtr = Jim_NewObj(interp); + objPtr->typePtr = &listObjType; + objPtr->bytes = NULL; + objPtr->internalRep.listValue.ele = NULL; + objPtr->internalRep.listValue.len = 0; + objPtr->internalRep.listValue.maxLen = 0; + for (i = 0; i < len; i++) { + ListAppendElement(objPtr, elements[i]); + } + return objPtr; +} + +/* Return a vector of Jim_Obj with the elements of a Jim list, and the + * length of the vector. Note that the user of this function should make + * sure that the list object can't shimmer while the vector returned + * is in use, this vector is the one stored inside the internal representation + * of the list object. This function is not exported, extensions should + * always access to the List object elements using Jim_ListIndex(). */ +static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen, + Jim_Obj ***listVec) +{ + *listLen = Jim_ListLength(interp, listObj); + *listVec = listObj->internalRep.listValue.ele; +} + +/* Sorting uses ints, but commands may return wide */ +static int JimSign(jim_wide w) +{ + if (w == 0) { + return 0; + } + else if (w < 0) { + return -1; + } + return 1; +} + +/* ListSortElements type values */ +struct lsort_info { + jmp_buf jmpbuf; + Jim_Obj *command; + Jim_Interp *interp; + enum { + JIM_LSORT_ASCII, + JIM_LSORT_NOCASE, + JIM_LSORT_INTEGER, + JIM_LSORT_COMMAND + } type; + int order; + int index; + int indexed; + int (*subfn)(Jim_Obj **, Jim_Obj **); +}; + +static struct lsort_info *sort_info; + +static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj) +{ + Jim_Obj *lObj, *rObj; + + if (Jim_ListIndex(sort_info->interp, *lhsObj, sort_info->index, &lObj, JIM_ERRMSG) != JIM_OK || + Jim_ListIndex(sort_info->interp, *rhsObj, sort_info->index, &rObj, JIM_ERRMSG) != JIM_OK) { + longjmp(sort_info->jmpbuf, JIM_ERR); + } + return sort_info->subfn(&lObj, &rObj); +} + +/* Sort the internal rep of a list. */ +static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj) +{ + return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order; +} + +static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj) +{ + return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order; +} + +static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj) +{ + jim_wide lhs = 0, rhs = 0; + + if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK || + Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) { + longjmp(sort_info->jmpbuf, JIM_ERR); + } + + return JimSign(lhs - rhs) * sort_info->order; +} + +static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj) +{ + Jim_Obj *compare_script; + int rc; + + jim_wide ret = 0; + + /* This must be a valid list */ + compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command); + Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj); + Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj); + + rc = Jim_EvalObj(sort_info->interp, compare_script); + + if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) { + longjmp(sort_info->jmpbuf, rc); + } + + return JimSign(ret) * sort_info->order; +} + +/* Sort a list *in place*. MUST be called with non-shared objects. */ +static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info) +{ + struct lsort_info *prev_info; + + typedef int (qsort_comparator) (const void *, const void *); + int (*fn) (Jim_Obj **, Jim_Obj **); + Jim_Obj **vector; + int len; + int rc; + + JimPanic((Jim_IsShared(listObjPtr), "Jim_ListSortElements called with shared object")); + if (!Jim_IsList(listObjPtr)) + SetListFromAny(interp, listObjPtr); + + /* Allow lsort to be called reentrantly */ + prev_info = sort_info; + sort_info = info; + + vector = listObjPtr->internalRep.listValue.ele; + len = listObjPtr->internalRep.listValue.len; + switch (info->type) { + case JIM_LSORT_ASCII: + fn = ListSortString; + break; + case JIM_LSORT_NOCASE: + fn = ListSortStringNoCase; + break; + case JIM_LSORT_INTEGER: + fn = ListSortInteger; + break; + case JIM_LSORT_COMMAND: + fn = ListSortCommand; + break; + default: + fn = NULL; /* avoid warning */ + JimPanic((1, "ListSort called with invalid sort type")); + } + + if (info->indexed) { + /* Need to interpose a "list index" function */ + info->subfn = fn; + fn = ListSortIndexHelper; + } + + if ((rc = setjmp(info->jmpbuf)) == 0) { + qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn); + } + Jim_InvalidateStringRep(listObjPtr); + sort_info = prev_info; + + return rc; +} + +/* This is the low-level function to insert elements into a list. + * The higher-level Jim_ListInsertElements() performs shared object + * check and invalidate the string repr. This version is used + * in the internals of the List Object and is not exported. + * + * NOTE: this function can be called only against objects + * with internal type of List. */ +static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec) +{ + int currentLen = listPtr->internalRep.listValue.len; + int requiredLen = currentLen + elemc; + int i; + Jim_Obj **point; + + if (requiredLen > listPtr->internalRep.listValue.maxLen) { + int maxLen = requiredLen * 2; + + listPtr->internalRep.listValue.ele = + Jim_Realloc(listPtr->internalRep.listValue.ele, sizeof(Jim_Obj *) * maxLen); + listPtr->internalRep.listValue.maxLen = maxLen; + } + point = listPtr->internalRep.listValue.ele + idx; + memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *)); + for (i = 0; i < elemc; ++i) { + point[i] = elemVec[i]; + Jim_IncrRefCount(point[i]); + } + listPtr->internalRep.listValue.len += elemc; +} + +/* Convenience call to ListInsertElements() to append a single element. + */ +static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr) +{ + ListInsertElements(listPtr, listPtr->internalRep.listValue.len, 1, &objPtr); +} + + +/* Appends every element of appendListPtr into listPtr. + * Both have to be of the list type. + * Convenience call to ListInsertElements() + */ +static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr) +{ + ListInsertElements(listPtr, listPtr->internalRep.listValue.len, + appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele); +} + +void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr) +{ + JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object")); + if (!Jim_IsList(listPtr)) + SetListFromAny(interp, listPtr); + Jim_InvalidateStringRep(listPtr); + ListAppendElement(listPtr, objPtr); +} + +void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr) +{ + JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object")); + if (!Jim_IsList(listPtr)) + SetListFromAny(interp, listPtr); + Jim_InvalidateStringRep(listPtr); + ListAppendList(listPtr, appendListPtr); +} + +int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr) +{ + if (!Jim_IsList(objPtr)) + SetListFromAny(interp, objPtr); + return objPtr->internalRep.listValue.len; +} + +void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx, + int objc, Jim_Obj *const *objVec) +{ + JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object")); + if (!Jim_IsList(listPtr)) + SetListFromAny(interp, listPtr); + if (idx >= 0 && idx > listPtr->internalRep.listValue.len) + idx = listPtr->internalRep.listValue.len; + else if (idx < 0) + idx = 0; + Jim_InvalidateStringRep(listPtr); + ListInsertElements(listPtr, idx, objc, objVec); +} + +int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags) +{ + if (!Jim_IsList(listPtr)) + SetListFromAny(interp, listPtr); + if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) || + (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) { + if (flags & JIM_ERRMSG) { + Jim_SetResultString(interp, "list index out of range", -1); + } + *objPtrPtr = NULL; + return JIM_ERR; + } + if (idx < 0) + idx = listPtr->internalRep.listValue.len + idx; + *objPtrPtr = listPtr->internalRep.listValue.ele[idx]; + return JIM_OK; +} + +static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, + Jim_Obj *newObjPtr, int flags) +{ + if (!Jim_IsList(listPtr)) + SetListFromAny(interp, listPtr); + if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) || + (idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) { + if (flags & JIM_ERRMSG) { + Jim_SetResultString(interp, "list index out of range", -1); + } + return JIM_ERR; + } + if (idx < 0) + idx = listPtr->internalRep.listValue.len + idx; + Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]); + listPtr->internalRep.listValue.ele[idx] = newObjPtr; + Jim_IncrRefCount(newObjPtr); + return JIM_OK; +} + +/* Modify the list stored into the variable named 'varNamePtr' + * setting the element specified by the 'indexc' indexes objects in 'indexv', + * with the new element 'newObjptr'. */ +int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr, + Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr) +{ + Jim_Obj *varObjPtr, *objPtr, *listObjPtr; + int shared, i, idx; + + varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED); + if (objPtr == NULL) + return JIM_ERR; + if ((shared = Jim_IsShared(objPtr))) + varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr); + for (i = 0; i < indexc - 1; i++) { + listObjPtr = objPtr; + if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK) + goto err; + if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_ERRMSG) != JIM_OK) { + goto err; + } + if (Jim_IsShared(objPtr)) { + objPtr = Jim_DuplicateObj(interp, objPtr); + ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE); + } + Jim_InvalidateStringRep(listObjPtr); + } + if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK) + goto err; + if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR) + goto err; + Jim_InvalidateStringRep(objPtr); + Jim_InvalidateStringRep(varObjPtr); + if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) + goto err; + Jim_SetResult(interp, varObjPtr); + return JIM_OK; + err: + if (shared) { + Jim_FreeNewObj(interp, varObjPtr); + } + return JIM_ERR; +} + +Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv) +{ + int i; + + /* If all the objects in objv are lists, + * it's possible to return a list as result, that's the + * concatenation of all the lists. */ + for (i = 0; i < objc; i++) { + if (!Jim_IsList(objv[i])) + break; + } + if (i == objc) { + Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0); + + for (i = 0; i < objc; i++) + Jim_ListAppendList(interp, objPtr, objv[i]); + return objPtr; + } + else { + /* Else... we have to glue strings together */ + int len = 0, objLen; + char *bytes, *p; + + /* Compute the length */ + for (i = 0; i < objc; i++) { + Jim_GetString(objv[i], &objLen); + len += objLen; + } + if (objc) + len += objc - 1; + /* Create the string rep, and a string object holding it. */ + p = bytes = Jim_Alloc(len + 1); + for (i = 0; i < objc; i++) { + const char *s = Jim_GetString(objv[i], &objLen); + + /* Remove leading space */ + while (objLen && (*s == ' ' || *s == '\t' || *s == '\n')) { + s++; + objLen--; + len--; + } + /* And trailing space */ + while (objLen && (s[objLen - 1] == ' ' || + s[objLen - 1] == '\n' || s[objLen - 1] == '\t')) { + /* Handle trailing backslash-space case */ + if (objLen > 1 && s[objLen - 2] == '\\') { + break; + } + objLen--; + len--; + } + memcpy(p, s, objLen); + p += objLen; + if (objLen && i + 1 != objc) { + *p++ = ' '; + } + else if (i + 1 != objc) { + /* Drop the space calcuated for this + * element that is instead null. */ + len--; + } + } + *p = '\0'; + return Jim_NewStringObjNoAlloc(interp, bytes, len); + } +} + +/* Returns a list composed of the elements in the specified range. + * first and start are directly accepted as Jim_Objects and + * processed for the end?-index? case. */ +Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, + Jim_Obj *lastObjPtr) +{ + int first, last; + int len, rangeLen; + + if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK || + Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK) + return NULL; + len = Jim_ListLength(interp, listObjPtr); /* will convert into list */ + first = JimRelToAbsIndex(len, first); + last = JimRelToAbsIndex(len, last); + JimRelToAbsRange(len, first, last, &first, &last, &rangeLen); + if (first == 0 && last == len) { + return listObjPtr; + } + return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen); +} + +/* ----------------------------------------------------------------------------- + * Dict object + * ---------------------------------------------------------------------------*/ +static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); +static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); +static void UpdateStringOfDict(struct Jim_Obj *objPtr); +static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); + +/* Dict HashTable Type. + * + * Keys and Values are Jim objects. */ + +static unsigned int JimObjectHTHashFunction(const void *key) +{ + const char *str; + Jim_Obj *objPtr = (Jim_Obj *)key; + int len; + + str = Jim_GetString(objPtr, &len); + return Jim_GenHashFunction((unsigned char *)str, len); +} + +static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2) +{ + JIM_NOTUSED(privdata); + + return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2); +} + +static void JimObjectHTKeyValDestructor(void *interp, void *val) +{ + Jim_Obj *objPtr = val; + + Jim_DecrRefCount(interp, objPtr); +} + +static const Jim_HashTableType JimDictHashTableType = { + JimObjectHTHashFunction, /* hash function */ + NULL, /* key dup */ + NULL, /* val dup */ + JimObjectHTKeyCompare, /* key compare */ + (void (*)(void *, const void *)) /* ATTENTION: const cast */ + JimObjectHTKeyValDestructor, /* key destructor */ + JimObjectHTKeyValDestructor /* val destructor */ +}; + +/* Note that while the elements of the dict may contain references, + * the list object itself can't. This basically means that the + * dict object string representation as a whole can't contain references + * that are not presents in the single elements. */ +static const Jim_ObjType dictObjType = { + "dict", + FreeDictInternalRep, + DupDictInternalRep, + UpdateStringOfDict, + JIM_TYPE_NONE, +}; + +void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) +{ + JIM_NOTUSED(interp); + + Jim_FreeHashTable(objPtr->internalRep.ptr); + Jim_Free(objPtr->internalRep.ptr); +} + +void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) +{ + Jim_HashTable *ht, *dupHt; + Jim_HashTableIterator *htiter; + Jim_HashEntry *he; + + /* Create a new hash table */ + ht = srcPtr->internalRep.ptr; + dupHt = Jim_Alloc(sizeof(*dupHt)); + Jim_InitHashTable(dupHt, &JimDictHashTableType, interp); + if (ht->size != 0) + Jim_ExpandHashTable(dupHt, ht->size); + /* Copy every element from the source to the dup hash table */ + htiter = Jim_GetHashTableIterator(ht); + while ((he = Jim_NextHashEntry(htiter)) != NULL) { + const Jim_Obj *keyObjPtr = he->key; + Jim_Obj *valObjPtr = he->u.val; + + Jim_IncrRefCount((Jim_Obj *)keyObjPtr); /* ATTENTION: const cast */ + Jim_IncrRefCount(valObjPtr); + Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr); + } + Jim_FreeHashTableIterator(htiter); + + dupPtr->internalRep.ptr = dupHt; + dupPtr->typePtr = &dictObjType; +} + +void UpdateStringOfDict(struct Jim_Obj *objPtr) +{ + int i, bufLen, realLength; + const char *strRep; + char *p; + int *quotingType, objc; + Jim_HashTable *ht; + Jim_HashTableIterator *htiter; + Jim_HashEntry *he; + Jim_Obj **objv; + + /* Trun the hash table into a flat vector of Jim_Objects. */ + ht = objPtr->internalRep.ptr; + objc = ht->used * 2; + objv = Jim_Alloc(objc * sizeof(Jim_Obj *)); + htiter = Jim_GetHashTableIterator(ht); + i = 0; + while ((he = Jim_NextHashEntry(htiter)) != NULL) { + objv[i++] = (Jim_Obj *)he->key; /* ATTENTION: const cast */ + objv[i++] = he->u.val; + } + Jim_FreeHashTableIterator(htiter); + /* (Over) Estimate the space needed. */ + quotingType = Jim_Alloc(sizeof(int) * objc); + bufLen = 0; + for (i = 0; i < objc; i++) { + int len; + + strRep = Jim_GetString(objv[i], &len); + quotingType[i] = ListElementQuotingType(strRep, len); + switch (quotingType[i]) { + case JIM_ELESTR_SIMPLE: + bufLen += len; + break; + case JIM_ELESTR_BRACE: + bufLen += len + 2; + break; + case JIM_ELESTR_QUOTE: + bufLen += len * 2; + break; + } + bufLen++; /* elements separator. */ + } + bufLen++; + + /* Generate the string rep. */ + p = objPtr->bytes = Jim_Alloc(bufLen + 1); + realLength = 0; + for (i = 0; i < objc; i++) { + int len, qlen; + char *q; + + strRep = Jim_GetString(objv[i], &len); + + switch (quotingType[i]) { + case JIM_ELESTR_SIMPLE: + memcpy(p, strRep, len); + p += len; + realLength += len; + break; + case JIM_ELESTR_BRACE: + *p++ = '{'; + memcpy(p, strRep, len); + p += len; + *p++ = '}'; + realLength += len + 2; + break; + case JIM_ELESTR_QUOTE: + q = BackslashQuoteString(strRep, len, &qlen); + memcpy(p, q, qlen); + Jim_Free(q); + p += qlen; + realLength += qlen; + break; + } + /* Add a separating space */ + if (i + 1 != objc) { + *p++ = ' '; + realLength++; + } + } + *p = '\0'; /* nul term. */ + objPtr->length = realLength; + Jim_Free(quotingType); + Jim_Free(objv); +} + +static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) +{ + int listlen; + + /* Get the string representation. Do this first so we don't + * change order in case of fast conversion to dict. + */ + Jim_String(objPtr); + + /* For simplicity, convert a non-list object to a list and then to a dict */ + listlen = Jim_ListLength(interp, objPtr); + if (listlen % 2) { + Jim_SetResultString(interp, + "invalid dictionary value: must be a list with an even number of elements", -1); + return JIM_ERR; + } + else { + /* Now it is easy to convert to a dict from a list, and it can't fail */ + Jim_HashTable *ht; + int i; + + ht = Jim_Alloc(sizeof(*ht)); + Jim_InitHashTable(ht, &JimDictHashTableType, interp); + + for (i = 0; i < listlen; i += 2) { + Jim_Obj *keyObjPtr; + Jim_Obj *valObjPtr; + + Jim_ListIndex(interp, objPtr, i, &keyObjPtr, JIM_NONE); + Jim_ListIndex(interp, objPtr, i + 1, &valObjPtr, JIM_NONE); + + Jim_IncrRefCount(keyObjPtr); + Jim_IncrRefCount(valObjPtr); + + if (Jim_AddHashEntry(ht, keyObjPtr, valObjPtr) != JIM_OK) { + Jim_HashEntry *he; + + he = Jim_FindHashEntry(ht, keyObjPtr); + Jim_DecrRefCount(interp, keyObjPtr); + /* ATTENTION: const cast */ + Jim_DecrRefCount(interp, (Jim_Obj *)he->u.val); + he->u.val = valObjPtr; + } + } + + Jim_FreeIntRep(interp, objPtr); + objPtr->typePtr = &dictObjType; + objPtr->internalRep.ptr = ht; + + return JIM_OK; + } +} + +/* Dict object API */ + +/* Add an element to a dict. objPtr must be of the "dict" type. + * The higer-level exported function is Jim_DictAddElement(). + * If an element with the specified key already exists, the value + * associated is replaced with the new one. + * + * if valueObjPtr == NULL, the key is instead removed if it exists. */ +static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, + Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr) +{ + Jim_HashTable *ht = objPtr->internalRep.ptr; + + if (valueObjPtr == NULL) { /* unset */ + return Jim_DeleteHashEntry(ht, keyObjPtr); + } + Jim_IncrRefCount(keyObjPtr); + Jim_IncrRefCount(valueObjPtr); + if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) { + Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr); + + Jim_DecrRefCount(interp, keyObjPtr); + /* ATTENTION: const cast */ + Jim_DecrRefCount(interp, (Jim_Obj *)he->u.val); + he->u.val = valueObjPtr; + } + return JIM_OK; +} + +/* Add an element, higher-level interface for DictAddElement(). + * If valueObjPtr == NULL, the key is removed if it exists. */ +int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, + Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr) +{ + int retcode; + + JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object")); + if (objPtr->typePtr != &dictObjType) { + if (SetDictFromAny(interp, objPtr) != JIM_OK) + return JIM_ERR; + } + retcode = DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr); + Jim_InvalidateStringRep(objPtr); + return retcode; +} + +Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len) +{ + Jim_Obj *objPtr; + int i; + + JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even")); + + objPtr = Jim_NewObj(interp); + objPtr->typePtr = &dictObjType; + objPtr->bytes = NULL; + objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable)); + Jim_InitHashTable(objPtr->internalRep.ptr, &JimDictHashTableType, interp); + for (i = 0; i < len; i += 2) + DictAddElement(interp, objPtr, elements[i], elements[i + 1]); + return objPtr; +} + +/* Return the value associated to the specified dict key + * Note: Returns JIM_OK if OK, JIM_ERR if entry not found or -1 if can't create dict value + */ +int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr, + Jim_Obj **objPtrPtr, int flags) +{ + Jim_HashEntry *he; + Jim_HashTable *ht; + + if (dictPtr->typePtr != &dictObjType) { + if (SetDictFromAny(interp, dictPtr) != JIM_OK) + return -1; + } + ht = dictPtr->internalRep.ptr; + if ((he = Jim_FindHashEntry(ht, keyPtr)) == NULL) { + if (flags & JIM_ERRMSG) { + Jim_SetResultFormatted(interp, "key \"%#s\" not found in dictionary", keyPtr); + } + return JIM_ERR; + } + *objPtrPtr = he->u.val; + return JIM_OK; +} + +/* Return an allocated array of key/value pairs for the dictionary. Stores the length in *len */ +int Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len) +{ + Jim_HashTable *ht; + Jim_HashTableIterator *htiter; + Jim_HashEntry *he; + Jim_Obj **objv; + int i; + + if (dictPtr->typePtr != &dictObjType) { + if (SetDictFromAny(interp, dictPtr) != JIM_OK) + return JIM_ERR; + } + ht = dictPtr->internalRep.ptr; + + /* Turn the hash table into a flat vector of Jim_Objects. */ + objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *)); + htiter = Jim_GetHashTableIterator(ht); + i = 0; + while ((he = Jim_NextHashEntry(htiter)) != NULL) { + objv[i++] = (Jim_Obj *)he->key; /* ATTENTION: const cast */ + objv[i++] = he->u.val; + } + *len = i; + Jim_FreeHashTableIterator(htiter); + *objPtrPtr = objv; + return JIM_OK; +} + + +/* Return the value associated to the specified dict keys */ +int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr, + Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags) +{ + int i; + + if (keyc == 0) { + *objPtrPtr = dictPtr; + return JIM_OK; + } + + for (i = 0; i < keyc; i++) { + Jim_Obj *objPtr; + + if (Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags) + != JIM_OK) + return JIM_ERR; + dictPtr = objPtr; + } + *objPtrPtr = dictPtr; + return JIM_OK; +} + +/* Modify the dict stored into the variable named 'varNamePtr' + * setting the element specified by the 'keyc' keys objects in 'keyv', + * with the new value of the element 'newObjPtr'. + * + * If newObjPtr == NULL the operation is to remove the given key + * from the dictionary. + * + * If flags & JIM_ERRMSG, then failure to remove the key is considered an error + * and JIM_ERR is returned. Otherwise it is ignored and JIM_OK is returned. + */ +int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr, + Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags) +{ + Jim_Obj *varObjPtr, *objPtr, *dictObjPtr; + int shared, i; + + varObjPtr = objPtr = + Jim_GetVariable(interp, varNamePtr, newObjPtr == NULL ? JIM_ERRMSG : JIM_NONE); + if (objPtr == NULL) { + if (newObjPtr == NULL) /* Cannot remove a key from non existing var */ { + return JIM_ERR; + } + varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0); + if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) { + Jim_FreeNewObj(interp, varObjPtr); + return JIM_ERR; + } + } + if ((shared = Jim_IsShared(objPtr))) + varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr); + for (i = 0; i < keyc - 1; i++) { + dictObjPtr = objPtr; + + /* Check if it's a valid dictionary */ + if (dictObjPtr->typePtr != &dictObjType) { + if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) { + goto err; + } + } + /* Check if the given key exists. */ + Jim_InvalidateStringRep(dictObjPtr); + if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr, + newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) { + /* This key exists at the current level. + * Make sure it's not shared!. */ + if (Jim_IsShared(objPtr)) { + objPtr = Jim_DuplicateObj(interp, objPtr); + DictAddElement(interp, dictObjPtr, keyv[i], objPtr); + } + } + else { + /* Key not found. If it's an [unset] operation + * this is an error. Only the last key may not + * exist. */ + if (newObjPtr == NULL) { + goto err; + } + /* Otherwise set an empty dictionary + * as key's value. */ + objPtr = Jim_NewDictObj(interp, NULL, 0); + DictAddElement(interp, dictObjPtr, keyv[i], objPtr); + } + } + /* Note error on unset with missing last key is OK */ + if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) { + if (newObjPtr || (flags & JIM_ERRMSG)) { + goto err; + } + } + Jim_InvalidateStringRep(objPtr); + Jim_InvalidateStringRep(varObjPtr); + if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) { + goto err; + } + Jim_SetResult(interp, varObjPtr); + return JIM_OK; + err: + if (shared) { + Jim_FreeNewObj(interp, varObjPtr); + } + return JIM_ERR; +} + +/* ----------------------------------------------------------------------------- + * Index object + * ---------------------------------------------------------------------------*/ +static void UpdateStringOfIndex(struct Jim_Obj *objPtr); +static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); + +static const Jim_ObjType indexObjType = { + "index", + NULL, + NULL, + UpdateStringOfIndex, + JIM_TYPE_NONE, +}; + +void UpdateStringOfIndex(struct Jim_Obj *objPtr) +{ + int len; + char buf[JIM_INTEGER_SPACE + 1]; + + if (objPtr->internalRep.indexValue >= 0) + len = sprintf(buf, "%d", objPtr->internalRep.indexValue); + else if (objPtr->internalRep.indexValue == -1) + len = sprintf(buf, "end"); + else { + len = sprintf(buf, "end%d", objPtr->internalRep.indexValue + 1); + } + objPtr->bytes = Jim_Alloc(len + 1); + memcpy(objPtr->bytes, buf, len + 1); + objPtr->length = len; +} + +int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr) +{ + int idx, end = 0; + const char *str; + char *endptr; + + /* Get the string representation */ + str = Jim_String(objPtr); + + /* Try to convert into an index */ + if (strncmp(str, "end", 3) == 0) { + end = 1; + str += 3; + idx = 0; + } + else { + idx = strtol(str, &endptr, 10); + + if (endptr == str) { + goto badindex; + } + str = endptr; + } + + /* Now str may include or + or - */ + if (*str == '+' || *str == '-') { + int sign = (*str == '+' ? 1 : -1); + + idx += sign * strtol(++str, &endptr, 10); + if (str == endptr || *endptr) { + goto badindex; + } + str = endptr; + } + /* The only thing left should be spaces */ + while (isspace(UCHAR(*str))) { + str++; + } + if (*str) { + goto badindex; + } + if (end) { + if (idx > 0) { + idx = INT_MAX; + } + else { + /* end-1 is repesented as -2 */ + idx--; + } + } + else if (idx < 0) { + idx = -INT_MAX; + } + + /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); + objPtr->typePtr = &indexObjType; + objPtr->internalRep.indexValue = idx; + return JIM_OK; + + badindex: + Jim_SetResultFormatted(interp, + "bad index \"%#s\": must be integer?[+-]integer? or end?[+-]integer?", objPtr); + return JIM_ERR; +} + +int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr) +{ + /* Avoid shimmering if the object is an integer. */ + if (objPtr->typePtr == &intObjType) { + jim_wide val = JimWideValue(objPtr); + + if (!(val < LONG_MIN) && !(val > LONG_MAX)) { + *indexPtr = (val < 0) ? -INT_MAX : (long)val;; + return JIM_OK; + } + } + if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR) + return JIM_ERR; + *indexPtr = objPtr->internalRep.indexValue; + return JIM_OK; +} + +/* ----------------------------------------------------------------------------- + * Return Code Object. + * ---------------------------------------------------------------------------*/ + +/* NOTE: These must be kept in the same order as JIM_OK, JIM_ERR, ... */ +static const char * const jimReturnCodes[] = { + [JIM_OK] = "ok", + [JIM_ERR] = "error", + [JIM_RETURN] = "return", + [JIM_BREAK] = "break", + [JIM_CONTINUE] = "continue", + [JIM_SIGNAL] = "signal", + [JIM_EXIT] = "exit", + [JIM_EVAL] = "eval", + NULL +}; + +#define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes)) + +static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr); + +static const Jim_ObjType returnCodeObjType = { + "return-code", + NULL, + NULL, + NULL, + JIM_TYPE_NONE, +}; + +/* Converts a (standard) return code to a string. Returns "?" for + * non-standard return codes. + */ +const char *Jim_ReturnCode(int code) +{ + if (code < 0 || code >= (int)jimReturnCodesSize) { + return "?"; + } + else { + return jimReturnCodes[code]; + } +} + +int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr) +{ + int returnCode; + jim_wide wideValue; + + /* Try to convert into an integer */ + if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR) + returnCode = (int)wideValue; + else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) { + Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr); + return JIM_ERR; + } + /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); + objPtr->typePtr = &returnCodeObjType; + objPtr->internalRep.returnCode = returnCode; + return JIM_OK; +} + +int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr) +{ + if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR) + return JIM_ERR; + *intPtr = objPtr->internalRep.returnCode; + return JIM_OK; +} + +/* ----------------------------------------------------------------------------- + * Expression Parsing + * ---------------------------------------------------------------------------*/ +static int JimParseExprOperator(struct JimParserCtx *pc); +static int JimParseExprNumber(struct JimParserCtx *pc); +static int JimParseExprIrrational(struct JimParserCtx *pc); + +/* Exrp's Stack machine operators opcodes. */ + +/* Binary operators (numbers) */ +enum +{ + /* Continues on from the JIM_TT_ space */ + /* Operations */ + JIM_EXPROP_MUL = JIM_TT_EXPR_OP, /* 15 */ + JIM_EXPROP_DIV, + JIM_EXPROP_MOD, + JIM_EXPROP_SUB, + JIM_EXPROP_ADD, + JIM_EXPROP_LSHIFT, + JIM_EXPROP_RSHIFT, + JIM_EXPROP_ROTL, + JIM_EXPROP_ROTR, + JIM_EXPROP_LT, + JIM_EXPROP_GT, + JIM_EXPROP_LTE, + JIM_EXPROP_GTE, + JIM_EXPROP_NUMEQ, + JIM_EXPROP_NUMNE, + JIM_EXPROP_BITAND, /* 30 */ + JIM_EXPROP_BITXOR, + JIM_EXPROP_BITOR, + + /* Note must keep these together */ + JIM_EXPROP_LOGICAND, /* 33 */ + JIM_EXPROP_LOGICAND_LEFT, + JIM_EXPROP_LOGICAND_RIGHT, + + /* and these */ + JIM_EXPROP_LOGICOR, /* 36 */ + JIM_EXPROP_LOGICOR_LEFT, + JIM_EXPROP_LOGICOR_RIGHT, + + /* and these */ + /* Ternary operators */ + JIM_EXPROP_TERNARY, /* 39 */ + JIM_EXPROP_TERNARY_LEFT, + JIM_EXPROP_TERNARY_RIGHT, + + /* and these */ + JIM_EXPROP_COLON, /* 42 */ + JIM_EXPROP_COLON_LEFT, + JIM_EXPROP_COLON_RIGHT, + + JIM_EXPROP_POW, /* 45 */ + +/* Binary operators (strings) */ + JIM_EXPROP_STREQ, + JIM_EXPROP_STRNE, + JIM_EXPROP_STRIN, + JIM_EXPROP_STRNI, + +/* Unary operators (numbers) */ + JIM_EXPROP_NOT, + JIM_EXPROP_BITNOT, + JIM_EXPROP_UNARYMINUS, + JIM_EXPROP_UNARYPLUS, + + /* Functions */ + JIM_EXPROP_FUNC_FIRST, + JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST, + JIM_EXPROP_FUNC_ABS, + JIM_EXPROP_FUNC_DOUBLE, + JIM_EXPROP_FUNC_ROUND, + JIM_EXPROP_FUNC_RAND, + JIM_EXPROP_FUNC_SRAND, + + /* math functions from libm */ + JIM_EXPROP_FUNC_SIN, + JIM_EXPROP_FUNC_COS, + JIM_EXPROP_FUNC_TAN, + JIM_EXPROP_FUNC_ASIN, + JIM_EXPROP_FUNC_ACOS, + JIM_EXPROP_FUNC_ATAN, + JIM_EXPROP_FUNC_SINH, + JIM_EXPROP_FUNC_COSH, + JIM_EXPROP_FUNC_TANH, + JIM_EXPROP_FUNC_CEIL, + JIM_EXPROP_FUNC_FLOOR, + JIM_EXPROP_FUNC_EXP, + JIM_EXPROP_FUNC_LOG, + JIM_EXPROP_FUNC_LOG10, + JIM_EXPROP_FUNC_SQRT, + JIM_EXPROP_FUNC_POW, +}; + +struct JimExprState +{ + Jim_Obj **stack; + int stacklen; + int opcode; + int skip; +}; + +/* Operators table */ +typedef struct Jim_ExprOperator +{ + const char *name; + int precedence; + int arity; + int (*funcop) (Jim_Interp *interp, struct JimExprState * e); + int lazy; +} Jim_ExprOperator; + +static void ExprPush(struct JimExprState *e, Jim_Obj *obj) +{ + Jim_IncrRefCount(obj); + e->stack[e->stacklen++] = obj; +} + +static Jim_Obj *ExprPop(struct JimExprState *e) +{ + return e->stack[--e->stacklen]; +} + +static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e) +{ + int intresult = 0; + int rc = JIM_OK; + Jim_Obj *A = ExprPop(e); + double dA, dC = 0; + jim_wide wA, wC = 0; + + if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) { + intresult = 1; + + switch (e->opcode) { + case JIM_EXPROP_FUNC_INT: + wC = wA; + break; + case JIM_EXPROP_FUNC_ROUND: + wC = wA; + break; + case JIM_EXPROP_FUNC_DOUBLE: + dC = wA; + intresult = 0; + break; + case JIM_EXPROP_FUNC_ABS: + wC = wA >= 0 ? wA : -wA; + break; + case JIM_EXPROP_UNARYMINUS: + wC = -wA; + break; + case JIM_EXPROP_UNARYPLUS: + wC = wA; + break; + case JIM_EXPROP_NOT: + wC = !wA; + break; + default: + abort(); + } + } + else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) { + switch (e->opcode) { + case JIM_EXPROP_FUNC_INT: + wC = dA; + intresult = 1; + break; + case JIM_EXPROP_FUNC_ROUND: + wC = dA < 0 ? (dA - 0.5) : (dA + 0.5); + intresult = 1; + break; + case JIM_EXPROP_FUNC_DOUBLE: + dC = dA; + break; + case JIM_EXPROP_FUNC_ABS: + dC = dA >= 0 ? dA : -dA; + break; + case JIM_EXPROP_UNARYMINUS: + dC = -dA; + break; + case JIM_EXPROP_UNARYPLUS: + dC = dA; + break; + case JIM_EXPROP_NOT: + wC = !dA; + intresult = 1; + break; + default: + abort(); + } + } + + if (rc == JIM_OK) { + if (intresult) { + ExprPush(e, Jim_NewIntObj(interp, wC)); + } + else { + ExprPush(e, Jim_NewDoubleObj(interp, dC)); + } + } + + Jim_DecrRefCount(interp, A); + + return rc; +} + +static double JimRandDouble(Jim_Interp *interp) +{ + unsigned long x; + JimRandomBytes(interp, &x, sizeof(x)); + + return (double)x / (unsigned long)~0; +} + +static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprState *e) +{ + Jim_Obj *A = ExprPop(e); + jim_wide wA; + + int rc = Jim_GetWide(interp, A, &wA); + if (rc == JIM_OK) { + switch (e->opcode) { + case JIM_EXPROP_BITNOT: + ExprPush(e, Jim_NewIntObj(interp, ~wA)); + break; + case JIM_EXPROP_FUNC_SRAND: + JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA)); + ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp))); + break; + default: + abort(); + } + } + + Jim_DecrRefCount(interp, A); + + return rc; +} + +static int JimExprOpNone(Jim_Interp *interp, struct JimExprState *e) +{ + JimPanic((e->opcode != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()")); + + ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp))); + + return JIM_OK; +} + +#ifdef JIM_MATH_FUNCTIONS +static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e) +{ + int rc; + Jim_Obj *A = ExprPop(e); + double dA, dC; + + rc = Jim_GetDouble(interp, A, &dA); + if (rc == JIM_OK) { + switch (e->opcode) { + case JIM_EXPROP_FUNC_SIN: + dC = sin(dA); + break; + case JIM_EXPROP_FUNC_COS: + dC = cos(dA); + break; + case JIM_EXPROP_FUNC_TAN: + dC = tan(dA); + break; + case JIM_EXPROP_FUNC_ASIN: + dC = asin(dA); + break; + case JIM_EXPROP_FUNC_ACOS: + dC = acos(dA); + break; + case JIM_EXPROP_FUNC_ATAN: + dC = atan(dA); + break; + case JIM_EXPROP_FUNC_SINH: + dC = sinh(dA); + break; + case JIM_EXPROP_FUNC_COSH: + dC = cosh(dA); + break; + case JIM_EXPROP_FUNC_TANH: + dC = tanh(dA); + break; + case JIM_EXPROP_FUNC_CEIL: + dC = ceil(dA); + break; + case JIM_EXPROP_FUNC_FLOOR: + dC = floor(dA); + break; + case JIM_EXPROP_FUNC_EXP: + dC = exp(dA); + break; + case JIM_EXPROP_FUNC_LOG: + dC = log(dA); + break; + case JIM_EXPROP_FUNC_LOG10: + dC = log10(dA); + break; + case JIM_EXPROP_FUNC_SQRT: + dC = sqrt(dA); + break; + default: + abort(); + } + ExprPush(e, Jim_NewDoubleObj(interp, dC)); + } + + Jim_DecrRefCount(interp, A); + + return rc; +} +#endif + +/* A binary operation on two ints */ +static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e) +{ + Jim_Obj *B = ExprPop(e); + Jim_Obj *A = ExprPop(e); + jim_wide wA, wB; + int rc = JIM_ERR; + + if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) { + jim_wide wC; + + rc = JIM_OK; + + switch (e->opcode) { + case JIM_EXPROP_LSHIFT: + wC = wA << wB; + break; + case JIM_EXPROP_RSHIFT: + wC = wA >> wB; + break; + case JIM_EXPROP_BITAND: + wC = wA & wB; + break; + case JIM_EXPROP_BITXOR: + wC = wA ^ wB; + break; + case JIM_EXPROP_BITOR: + wC = wA | wB; + break; + case JIM_EXPROP_MOD: + if (wB == 0) { + wC = 0; + Jim_SetResultString(interp, "Division by zero", -1); + rc = JIM_ERR; + } + else { + /* + * From Tcl 8.x + * + * This code is tricky: C doesn't guarantee much + * about the quotient or remainder, but Tcl does. + * The remainder always has the same sign as the + * divisor and a smaller absolute value. + */ + int negative = 0; + + if (wB < 0) { + wB = -wB; + wA = -wA; + negative = 1; + } + wC = wA % wB; + if (wC < 0) { + wC += wB; + } + if (negative) { + wC = -wC; + } + } + break; + case JIM_EXPROP_ROTL: + case JIM_EXPROP_ROTR:{ + /* uint32_t would be better. But not everyone has inttypes.h? */ + unsigned long uA = (unsigned long)wA; + unsigned long uB = (unsigned long)wB; + const unsigned int S = sizeof(unsigned long) * 8; + + /* Shift left by the word size or more is undefined. */ + uB %= S; + + if (e->opcode == JIM_EXPROP_ROTR) { + uB = S - uB; + } + wC = (unsigned long)(uA << uB) | (uA >> (S - uB)); + break; + } + default: + abort(); + } + ExprPush(e, Jim_NewIntObj(interp, wC)); + + } + + Jim_DecrRefCount(interp, A); + Jim_DecrRefCount(interp, B); + + return rc; +} + + +/* A binary operation on two ints or two doubles (or two strings for some ops) */ +static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e) +{ + int intresult = 0; + int rc = JIM_OK; + double dA, dB, dC = 0; + jim_wide wA, wB, wC = 0; + + Jim_Obj *B = ExprPop(e); + Jim_Obj *A = ExprPop(e); + + if ((A->typePtr != &doubleObjType || A->bytes) && + (B->typePtr != &doubleObjType || B->bytes) && + JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) { + + /* Both are ints */ + + intresult = 1; + + switch (e->opcode) { + case JIM_EXPROP_POW: + case JIM_EXPROP_FUNC_POW: + wC = JimPowWide(wA, wB); + break; + case JIM_EXPROP_ADD: + wC = wA + wB; + break; + case JIM_EXPROP_SUB: + wC = wA - wB; + break; + case JIM_EXPROP_MUL: + wC = wA * wB; + break; + case JIM_EXPROP_DIV: + if (wB == 0) { + Jim_SetResultString(interp, "Division by zero", -1); + rc = JIM_ERR; + } + else { + /* + * From Tcl 8.x + * + * This code is tricky: C doesn't guarantee much + * about the quotient or remainder, but Tcl does. + * The remainder always has the same sign as the + * divisor and a smaller absolute value. + */ + if (wB < 0) { + wB = -wB; + wA = -wA; + } + wC = wA / wB; + if (wA % wB < 0) { + wC--; + } + } + break; + case JIM_EXPROP_LT: + wC = wA < wB; + break; + case JIM_EXPROP_GT: + wC = wA > wB; + break; + case JIM_EXPROP_LTE: + wC = wA <= wB; + break; + case JIM_EXPROP_GTE: + wC = wA >= wB; + break; + case JIM_EXPROP_NUMEQ: + wC = wA == wB; + break; + case JIM_EXPROP_NUMNE: + wC = wA != wB; + break; + default: + abort(); + } + } + else if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) { + switch (e->opcode) { + case JIM_EXPROP_POW: + case JIM_EXPROP_FUNC_POW: +#ifdef JIM_MATH_FUNCTIONS + dC = pow(dA, dB); +#else + Jim_SetResultString(interp, "unsupported", -1); + rc = JIM_ERR; +#endif + break; + case JIM_EXPROP_ADD: + dC = dA + dB; + break; + case JIM_EXPROP_SUB: + dC = dA - dB; + break; + case JIM_EXPROP_MUL: + dC = dA * dB; + break; + case JIM_EXPROP_DIV: + if (dB == 0) { +#ifdef INFINITY + dC = dA < 0 ? -INFINITY : INFINITY; +#else + dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL); +#endif + } + else { + dC = dA / dB; + } + break; + case JIM_EXPROP_LT: + wC = dA < dB; + intresult = 1; + break; + case JIM_EXPROP_GT: + wC = dA > dB; + intresult = 1; + break; + case JIM_EXPROP_LTE: + wC = dA <= dB; + intresult = 1; + break; + case JIM_EXPROP_GTE: + wC = dA >= dB; + intresult = 1; + break; + case JIM_EXPROP_NUMEQ: + wC = dA == dB; + intresult = 1; + break; + case JIM_EXPROP_NUMNE: + wC = dA != dB; + intresult = 1; + break; + default: + abort(); + } + } + else { + /* Handle the string case */ + + /* REVISIT: Could optimise the eq/ne case by checking lengths */ + int i = Jim_StringCompareObj(interp, A, B, 0); + + intresult = 1; + + switch (e->opcode) { + case JIM_EXPROP_LT: + wC = i < 0; + break; + case JIM_EXPROP_GT: + wC = i > 0; + break; + case JIM_EXPROP_LTE: + wC = i <= 0; + break; + case JIM_EXPROP_GTE: + wC = i >= 0; + break; + case JIM_EXPROP_NUMEQ: + wC = i == 0; + break; + case JIM_EXPROP_NUMNE: + wC = i != 0; + break; + default: + rc = JIM_ERR; + break; + } + } + + if (rc == JIM_OK) { + if (intresult) { + ExprPush(e, Jim_NewIntObj(interp, wC)); + } + else { + ExprPush(e, Jim_NewDoubleObj(interp, dC)); + } + } + + Jim_DecrRefCount(interp, A); + Jim_DecrRefCount(interp, B); + + return rc; +} + +static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj) +{ + int listlen; + int i; + + listlen = Jim_ListLength(interp, listObjPtr); + for (i = 0; i < listlen; i++) { + Jim_Obj *objPtr; + + Jim_ListIndex(interp, listObjPtr, i, &objPtr, JIM_NONE); + + if (Jim_StringEqObj(objPtr, valObj)) { + return 1; + } + } + return 0; +} + +static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprState *e) +{ + Jim_Obj *B = ExprPop(e); + Jim_Obj *A = ExprPop(e); + + jim_wide wC; + + switch (e->opcode) { + case JIM_EXPROP_STREQ: + case JIM_EXPROP_STRNE: { + int Alen, Blen; + const char *sA = Jim_GetString(A, &Alen); + const char *sB = Jim_GetString(B, &Blen); + + if (e->opcode == JIM_EXPROP_STREQ) { + wC = (Alen == Blen && memcmp(sA, sB, Alen) == 0); + } + else { + wC = (Alen != Blen || memcmp(sA, sB, Alen) != 0); + } + break; + } + case JIM_EXPROP_STRIN: + wC = JimSearchList(interp, B, A); + break; + case JIM_EXPROP_STRNI: + wC = !JimSearchList(interp, B, A); + break; + default: + abort(); + } + ExprPush(e, Jim_NewIntObj(interp, wC)); + + Jim_DecrRefCount(interp, A); + Jim_DecrRefCount(interp, B); + + return JIM_OK; +} + +static int ExprBool(Jim_Interp *interp, Jim_Obj *obj) +{ + long l; + double d; + + if (Jim_GetLong(interp, obj, &l) == JIM_OK) { + return l != 0; + } + if (Jim_GetDouble(interp, obj, &d) == JIM_OK) { + return d != 0; + } + return -1; +} + +static int JimExprOpAndLeft(Jim_Interp *interp, struct JimExprState *e) +{ + Jim_Obj *skip = ExprPop(e); + Jim_Obj *A = ExprPop(e); + int rc = JIM_OK; + + switch (ExprBool(interp, A)) { + case 0: + /* false, so skip RHS opcodes with a 0 result */ + e->skip = JimWideValue(skip); + ExprPush(e, Jim_NewIntObj(interp, 0)); + break; + + case 1: + /* true so continue */ + break; + + case -1: + /* Invalid */ + rc = JIM_ERR; + } + Jim_DecrRefCount(interp, A); + Jim_DecrRefCount(interp, skip); + + return rc; +} + +static int JimExprOpOrLeft(Jim_Interp *interp, struct JimExprState *e) +{ + Jim_Obj *skip = ExprPop(e); + Jim_Obj *A = ExprPop(e); + int rc = JIM_OK; + + switch (ExprBool(interp, A)) { + case 0: + /* false, so do nothing */ + break; + + case 1: + /* true so skip RHS opcodes with a 1 result */ + e->skip = JimWideValue(skip); + ExprPush(e, Jim_NewIntObj(interp, 1)); + break; + + case -1: + /* Invalid */ + rc = JIM_ERR; + break; + } + Jim_DecrRefCount(interp, A); + Jim_DecrRefCount(interp, skip); + + return rc; +} + +static int JimExprOpAndOrRight(Jim_Interp *interp, struct JimExprState *e) +{ + Jim_Obj *A = ExprPop(e); + int rc = JIM_OK; + + switch (ExprBool(interp, A)) { + case 0: + ExprPush(e, Jim_NewIntObj(interp, 0)); + break; + + case 1: + ExprPush(e, Jim_NewIntObj(interp, 1)); + break; + + case -1: + /* Invalid */ + rc = JIM_ERR; + break; + } + Jim_DecrRefCount(interp, A); + + return rc; +} + +static int JimExprOpTernaryLeft(Jim_Interp *interp, struct JimExprState *e) +{ + Jim_Obj *skip = ExprPop(e); + Jim_Obj *A = ExprPop(e); + int rc = JIM_OK; + + /* Repush A */ + ExprPush(e, A); + + switch (ExprBool(interp, A)) { + case 0: + /* false, skip RHS opcodes */ + e->skip = JimWideValue(skip); + /* Push a dummy value */ + ExprPush(e, Jim_NewIntObj(interp, 0)); + break; + + case 1: + /* true so do nothing */ + break; + + case -1: + /* Invalid */ + rc = JIM_ERR; + break; + } + Jim_DecrRefCount(interp, A); + Jim_DecrRefCount(interp, skip); + + return rc; +} + +static int JimExprOpColonLeft(Jim_Interp *interp, struct JimExprState *e) +{ + Jim_Obj *skip = ExprPop(e); + Jim_Obj *B = ExprPop(e); + Jim_Obj *A = ExprPop(e); + + /* No need to check for A as non-boolean */ + if (ExprBool(interp, A)) { + /* true, so skip RHS opcodes */ + e->skip = JimWideValue(skip); + /* Repush B as the answer */ + ExprPush(e, B); + } + + Jim_DecrRefCount(interp, skip); + Jim_DecrRefCount(interp, A); + Jim_DecrRefCount(interp, B); + return JIM_OK; +} + +static int JimExprOpNull(Jim_Interp *interp, struct JimExprState *e) +{ + return JIM_OK; +} + +enum +{ + LAZY_NONE, + LAZY_OP, + LAZY_LEFT, + LAZY_RIGHT +}; + +/* name - precedence - arity - opcode */ +static const struct Jim_ExprOperator Jim_ExprOperators[] = { + [JIM_EXPROP_FUNC_INT] = {"int", 400, 1, JimExprOpNumUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_DOUBLE] = {"double", 400, 1, JimExprOpNumUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_ABS] = {"abs", 400, 1, JimExprOpNumUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_ROUND] = {"round", 400, 1, JimExprOpNumUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_RAND] = {"rand", 400, 0, JimExprOpNone, LAZY_NONE}, + [JIM_EXPROP_FUNC_SRAND] = {"srand", 400, 1, JimExprOpIntUnary, LAZY_NONE}, + +#ifdef JIM_MATH_FUNCTIONS + [JIM_EXPROP_FUNC_SIN] = {"sin", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_COS] = {"cos", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_TAN] = {"tan", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_ASIN] = {"asin", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_ACOS] = {"acos", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_ATAN] = {"atan", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_SINH] = {"sinh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_COSH] = {"cosh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_TANH] = {"tanh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_CEIL] = {"ceil", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_FLOOR] = {"floor", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_EXP] = {"exp", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_LOG] = {"log", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_LOG10] = {"log10", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_SQRT] = {"sqrt", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + [JIM_EXPROP_FUNC_POW] = {"pow", 400, 2, JimExprOpBin, LAZY_NONE}, +#endif + + [JIM_EXPROP_NOT] = {"!", 300, 1, JimExprOpNumUnary, LAZY_NONE}, + [JIM_EXPROP_BITNOT] = {"~", 300, 1, JimExprOpIntUnary, LAZY_NONE}, + [JIM_EXPROP_UNARYMINUS] = {NULL, 300, 1, JimExprOpNumUnary, LAZY_NONE}, + [JIM_EXPROP_UNARYPLUS] = {NULL, 300, 1, JimExprOpNumUnary, LAZY_NONE}, + + [JIM_EXPROP_POW] = {"**", 250, 2, JimExprOpBin, LAZY_NONE}, + + [JIM_EXPROP_MUL] = {"*", 200, 2, JimExprOpBin, LAZY_NONE}, + [JIM_EXPROP_DIV] = {"/", 200, 2, JimExprOpBin, LAZY_NONE}, + [JIM_EXPROP_MOD] = {"%", 200, 2, JimExprOpIntBin, LAZY_NONE}, + + [JIM_EXPROP_SUB] = {"-", 100, 2, JimExprOpBin, LAZY_NONE}, + [JIM_EXPROP_ADD] = {"+", 100, 2, JimExprOpBin, LAZY_NONE}, + + [JIM_EXPROP_ROTL] = {"<<<", 90, 2, JimExprOpIntBin, LAZY_NONE}, + [JIM_EXPROP_ROTR] = {">>>", 90, 2, JimExprOpIntBin, LAZY_NONE}, + [JIM_EXPROP_LSHIFT] = {"<<", 90, 2, JimExprOpIntBin, LAZY_NONE}, + [JIM_EXPROP_RSHIFT] = {">>", 90, 2, JimExprOpIntBin, LAZY_NONE}, + + [JIM_EXPROP_LT] = {"<", 80, 2, JimExprOpBin, LAZY_NONE}, + [JIM_EXPROP_GT] = {">", 80, 2, JimExprOpBin, LAZY_NONE}, + [JIM_EXPROP_LTE] = {"<=", 80, 2, JimExprOpBin, LAZY_NONE}, + [JIM_EXPROP_GTE] = {">=", 80, 2, JimExprOpBin, LAZY_NONE}, + + [JIM_EXPROP_NUMEQ] = {"==", 70, 2, JimExprOpBin, LAZY_NONE}, + [JIM_EXPROP_NUMNE] = {"!=", 70, 2, JimExprOpBin, LAZY_NONE}, + + [JIM_EXPROP_STREQ] = {"eq", 60, 2, JimExprOpStrBin, LAZY_NONE}, + [JIM_EXPROP_STRNE] = {"ne", 60, 2, JimExprOpStrBin, LAZY_NONE}, + + [JIM_EXPROP_STRIN] = {"in", 55, 2, JimExprOpStrBin, LAZY_NONE}, + [JIM_EXPROP_STRNI] = {"ni", 55, 2, JimExprOpStrBin, LAZY_NONE}, + + [JIM_EXPROP_BITAND] = {"&", 50, 2, JimExprOpIntBin, LAZY_NONE}, + [JIM_EXPROP_BITXOR] = {"^", 49, 2, JimExprOpIntBin, LAZY_NONE}, + [JIM_EXPROP_BITOR] = {"|", 48, 2, JimExprOpIntBin, LAZY_NONE}, + + [JIM_EXPROP_LOGICAND] = {"&&", 10, 2, NULL, LAZY_OP}, + [JIM_EXPROP_LOGICOR] = {"||", 9, 2, NULL, LAZY_OP}, + + [JIM_EXPROP_TERNARY] = {"?", 5, 2, JimExprOpNull, LAZY_OP}, + [JIM_EXPROP_COLON] = {":", 5, 2, JimExprOpNull, LAZY_OP}, + + /* private operators */ + [JIM_EXPROP_TERNARY_LEFT] = {NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT}, + [JIM_EXPROP_TERNARY_RIGHT] = {NULL, 5, 2, JimExprOpNull, LAZY_RIGHT}, + [JIM_EXPROP_COLON_LEFT] = {NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT}, + [JIM_EXPROP_COLON_RIGHT] = {NULL, 5, 2, JimExprOpNull, LAZY_RIGHT}, + [JIM_EXPROP_LOGICAND_LEFT] = {NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT}, + [JIM_EXPROP_LOGICAND_RIGHT] = {NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT}, + [JIM_EXPROP_LOGICOR_LEFT] = {NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT}, + [JIM_EXPROP_LOGICOR_RIGHT] = {NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT}, +}; + +#define JIM_EXPR_OPERATORS_NUM \ + (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator)) + +static int JimParseExpression(struct JimParserCtx *pc) +{ + /* Discard spaces and quoted newline */ + while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) { + if (*pc->p == '\n') { + pc->linenr++; + } + pc->p++; + pc->len--; + } + + if (pc->len == 0) { + pc->tstart = pc->tend = pc->p; + pc->tline = pc->linenr; + pc->tt = JIM_TT_EOL; + pc->eof = 1; + return JIM_OK; + } + switch (*(pc->p)) { + case '(': + pc->tt = JIM_TT_SUBEXPR_START; + goto singlechar; + case ')': + pc->tt = JIM_TT_SUBEXPR_END; + goto singlechar; + case ',': + pc->tt = JIM_TT_SUBEXPR_COMMA; +singlechar: + pc->tstart = pc->tend = pc->p; + pc->tline = pc->linenr; + pc->p++; + pc->len--; + break; + case '[': + return JimParseCmd(pc); + case '$': + if (JimParseVar(pc) == JIM_ERR) + return JimParseExprOperator(pc); + else { + /* Don't allow expr sugar in expressions */ + if (pc->tt == JIM_TT_EXPRSUGAR) { + return JIM_ERR; + } + return JIM_OK; + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '.': + return JimParseExprNumber(pc); + case '"': + return JimParseQuote(pc); + case '{': + return JimParseBrace(pc); + + case 'N': + case 'I': + case 'n': + case 'i': + if (JimParseExprIrrational(pc) == JIM_ERR) + return JimParseExprOperator(pc); + break; + default: + return JimParseExprOperator(pc); + break; + } + return JIM_OK; +} + +static int JimParseExprNumber(struct JimParserCtx *pc) +{ + int allowdot = 1; + int allowhex = 0; + + /* Assume an integer for now */ + pc->tt = JIM_TT_EXPR_INT; + pc->tstart = pc->p; + pc->tline = pc->linenr; + while (isdigit(UCHAR(*pc->p)) + || (allowhex && isxdigit(UCHAR(*pc->p))) + || (allowdot && *pc->p == '.') + || (pc->p - pc->tstart == 1 && *pc->tstart == '0' && (*pc->p == 'x' || *pc->p == 'X')) + ) { + if ((*pc->p == 'x') || (*pc->p == 'X')) { + allowhex = 1; + allowdot = 0; + } + if (*pc->p == '.') { + allowdot = 0; + pc->tt = JIM_TT_EXPR_DOUBLE; + } + pc->p++; + pc->len--; + if (!allowhex && (*pc->p == 'e' || *pc->p == 'E') && (pc->p[1] == '-' || pc->p[1] == '+' + || isdigit(UCHAR(pc->p[1])))) { + pc->p += 2; + pc->len -= 2; + pc->tt = JIM_TT_EXPR_DOUBLE; + } + } + pc->tend = pc->p - 1; + return JIM_OK; +} + +static int JimParseExprIrrational(struct JimParserCtx *pc) +{ + const char *Tokens[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL }; + const char **token; + + for (token = Tokens; *token != NULL; token++) { + int len = strlen(*token); + + if (strncmp(*token, pc->p, len) == 0) { + pc->tstart = pc->p; + pc->tend = pc->p + len - 1; + pc->p += len; + pc->len -= len; + pc->tline = pc->linenr; + pc->tt = JIM_TT_EXPR_DOUBLE; + return JIM_OK; + } + } + return JIM_ERR; +} + +static int JimParseExprOperator(struct JimParserCtx *pc) +{ + int i; + int bestIdx = -1, bestLen = 0; + + /* Try to get the longest match. */ + for (i = JIM_TT_EXPR_OP; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) { + const char *opname; + int oplen; + + opname = Jim_ExprOperators[i].name; + if (opname == NULL) { + continue; + } + oplen = strlen(opname); + + if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) { + bestIdx = i; + bestLen = oplen; + } + } + if (bestIdx == -1) { + return JIM_ERR; + } + + /* Validate paretheses around function arguments */ + if (bestIdx >= JIM_EXPROP_FUNC_FIRST) { + const char *p = pc->p + bestLen; + int len = pc->len - bestLen; + + while (len && isspace(UCHAR(*p))) { + len--; + p++; + } + if (*p != '(') { + return JIM_ERR; + } + } + pc->tstart = pc->p; + pc->tend = pc->p + bestLen - 1; + pc->p += bestLen; + pc->len -= bestLen; + pc->tline = pc->linenr; + + pc->tt = bestIdx; + return JIM_OK; +} + +static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode) +{ + return &Jim_ExprOperators[opcode]; +} + +const char *jim_tt_name(int type) +{ + static const char * const tt_names[JIM_TT_EXPR_OP] = + { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT", + "DBL", "$()" }; + if (type < JIM_TT_EXPR_OP) { + return tt_names[type]; + } + else { + const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(type); + static char buf[20]; + + if (op && op->name) { + return op->name; + } + sprintf(buf, "(%d)", type); + return buf; + } +} + +/* ----------------------------------------------------------------------------- + * Expression Object + * ---------------------------------------------------------------------------*/ +static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); +static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); +static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); + +static const Jim_ObjType exprObjType = { + "expression", + FreeExprInternalRep, + DupExprInternalRep, + NULL, + JIM_TYPE_REFERENCES, +}; + +/* Expr bytecode structure */ +typedef struct ExprByteCode +{ + int len; /* Length as number of tokens. */ + ScriptToken *token; /* Tokens array. */ + int inUse; /* Used for sharing. */ +} ExprByteCode; + +static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr) +{ + int i; + + for (i = 0; i < expr->len; i++) { + Jim_DecrRefCount(interp, expr->token[i].objPtr); + } + Jim_Free(expr->token); + Jim_Free(expr); +} + +static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) +{ + ExprByteCode *expr = (void *)objPtr->internalRep.ptr; + + if (expr) { + if (--expr->inUse != 0) { + return; + } + + ExprFreeByteCode(interp, expr); + } +} + +static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) +{ + JIM_NOTUSED(interp); + JIM_NOTUSED(srcPtr); + + /* Just returns an simple string. */ + dupPtr->typePtr = NULL; +} + +/* Check if an expr program looks correct. */ +static int ExprCheckCorrectness(ExprByteCode * expr) +{ + int i; + int stacklen = 0; + int ternary = 0; + + /* Try to check if there are stack underflows, + * and make sure at the end of the program there is + * a single result on the stack. */ + for (i = 0; i < expr->len; i++) { + ScriptToken *t = &expr->token[i]; + const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type); + + if (op) { + stacklen -= op->arity; + if (stacklen < 0) { + break; + } + if (t->type == JIM_EXPROP_TERNARY || t->type == JIM_EXPROP_TERNARY_LEFT) { + ternary++; + } + else if (t->type == JIM_EXPROP_COLON || t->type == JIM_EXPROP_COLON_LEFT) { + ternary--; + } + } + + /* All operations and operands add one to the stack */ + stacklen++; + } + if (stacklen != 1 || ternary != 0) { + return JIM_ERR; + } + return JIM_OK; +} + +/* This procedure converts every occurrence of || and && opereators + * in lazy unary versions. + * + * a b || is converted into: + * + * a |L b |R + * + * a b && is converted into: + * + * a &L b &R + * + * "|L" checks if 'a' is true: + * 1) if it is true pushes 1 and skips instructions to reach + * the opcode just after |R. + * 2) if it is false does nothing. + * "|R" checks if 'b' is true: + * 1) if it is true pushes 1, otherwise pushes 0. + * + * "&L" checks if 'a' is true: + * 1) if it is true does nothing. + * 2) If it is false pushes 0 and skips instructions to reach + * the opcode just after &R + * "&R" checks if 'a' is true: + * if it is true pushes 1, otherwise pushes 0. + */ +static int ExprAddLazyOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t) +{ + int i; + + int leftindex, arity, offset; + + /* Search for the end of the first operator */ + leftindex = expr->len - 1; + + arity = 1; + while (arity) { + ScriptToken *tt = &expr->token[leftindex]; + + if (tt->type >= JIM_TT_EXPR_OP) { + arity += JimExprOperatorInfoByOpcode(tt->type)->arity; + } + arity--; + if (--leftindex < 0) { + return JIM_ERR; + } + } + leftindex++; + + /* Move them up */ + memmove(&expr->token[leftindex + 2], &expr->token[leftindex], + sizeof(*expr->token) * (expr->len - leftindex)); + expr->len += 2; + offset = (expr->len - leftindex) - 1; + + /* Now we rely on the fact the the left and right version have opcodes + * 1 and 2 after the main opcode respectively + */ + expr->token[leftindex + 1].type = t->type + 1; + expr->token[leftindex + 1].objPtr = interp->emptyObj; + + expr->token[leftindex].type = JIM_TT_EXPR_INT; + expr->token[leftindex].objPtr = Jim_NewIntObj(interp, offset); + + /* Now add the 'R' operator */ + expr->token[expr->len].objPtr = interp->emptyObj; + expr->token[expr->len].type = t->type + 2; + expr->len++; + + /* Do we need to adjust the skip count for any &L, |L, ?L or :L in the left operand? */ + for (i = leftindex - 1; i > 0; i--) { + if (JimExprOperatorInfoByOpcode(expr->token[i].type)->lazy == LAZY_LEFT) { + if (JimWideValue(expr->token[i - 1].objPtr) + i - 1 >= leftindex) { + JimWideValue(expr->token[i - 1].objPtr) += 2; + } + } + } + return JIM_OK; +} + +static int ExprAddOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t) +{ + struct ScriptToken *token = &expr->token[expr->len]; + const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type); + + if (op->lazy == LAZY_OP) { + if (ExprAddLazyOperator(interp, expr, t) != JIM_OK) { + Jim_SetResultFormatted(interp, "Expression has bad operands to %s", op->name); + return JIM_ERR; + } + } + else { + token->objPtr = interp->emptyObj; + token->type = t->type; + expr->len++; + } + return JIM_OK; +} + +/** + * Returns the index of the COLON_LEFT to the left of 'right_index' + * taking into account nesting. + * + * The expression *must* be well formed, thus a COLON_LEFT will always be found. + */ +static int ExprTernaryGetColonLeftIndex(ExprByteCode *expr, int right_index) +{ + int ternary_count = 1; + + right_index--; + + while (right_index > 1) { + if (expr->token[right_index].type == JIM_EXPROP_TERNARY_LEFT) { + ternary_count--; + } + else if (expr->token[right_index].type == JIM_EXPROP_COLON_RIGHT) { + ternary_count++; + } + else if (expr->token[right_index].type == JIM_EXPROP_COLON_LEFT && ternary_count == 1) { + return right_index; + } + right_index--; + } + + /*notreached*/ + return -1; +} + +/** + * Find the left/right indices for the ternary expression to the left of 'right_index'. + * + * Returns 1 if found, and fills in *prev_right_index and *prev_left_index. + * Otherwise returns 0. + */ +static int ExprTernaryGetMoveIndices(ExprByteCode *expr, int right_index, int *prev_right_index, int *prev_left_index) +{ + int i = right_index - 1; + int ternary_count = 1; + + while (i > 1) { + if (expr->token[i].type == JIM_EXPROP_TERNARY_LEFT) { + if (--ternary_count == 0 && expr->token[i - 2].type == JIM_EXPROP_COLON_RIGHT) { + *prev_right_index = i - 2; + *prev_left_index = ExprTernaryGetColonLeftIndex(expr, *prev_right_index); + return 1; + } + } + else if (expr->token[i].type == JIM_EXPROP_COLON_RIGHT) { + if (ternary_count == 0) { + return 0; + } + ternary_count++; + } + i--; + } + return 0; +} + +/* +* ExprTernaryReorderExpression description +* ======================================== +* +* ?: is right-to-left associative which doesn't work with the stack-based +* expression engine. The fix is to reorder the bytecode. +* +* The expression: +* +* expr 1?2:0?3:4 +* +* Has initial bytecode: +* +* '1' '2' (40=TERNARY_LEFT) '2' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '0' (44=COLON_RIGHT) +* '2' (40=TERNARY_LEFT) '3' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '4' (44=COLON_RIGHT) +* +* The fix involves simulating this expression instead: +* +* expr 1?2:(0?3:4) +* +* With the following bytecode: +* +* '1' '2' (40=TERNARY_LEFT) '2' (41=TERNARY_RIGHT) '10' (43=COLON_LEFT) '0' '2' (40=TERNARY_LEFT) +* '3' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '4' (44=COLON_RIGHT) (44=COLON_RIGHT) +* +* i.e. The token COLON_RIGHT at index 8 is moved towards the end of the stack, all tokens above 8 +* are shifted down and the skip count of the token JIM_EXPROP_COLON_LEFT at index 5 is +* incremented by the amount tokens shifted down. The token JIM_EXPROP_COLON_RIGHT that is moved +* is identified as immediately preceeding a token JIM_EXPROP_TERNARY_LEFT +* +* ExprTernaryReorderExpression works thus as follows : +* - start from the end of the stack +* - while walking towards the beginning of the stack +* if token=JIM_EXPROP_COLON_RIGHT then +* find the associated token JIM_EXPROP_TERNARY_LEFT, which allows to +* find the associated token previous(JIM_EXPROP_COLON_RIGHT) +* find the associated token previous(JIM_EXPROP_LEFT_RIGHT) +* if all found then +* perform the rotation +* update the skip count of the token previous(JIM_EXPROP_LEFT_RIGHT) +* end if +* end if +* +* Note: care has to be taken for nested ternary constructs!!! +*/ +static void ExprTernaryReorderExpression(Jim_Interp *interp, ExprByteCode *expr) +{ + int i; + + for (i = expr->len - 1; i > 1; i--) { + int prev_right_index; + int prev_left_index; + int j; + ScriptToken tmp; + + if (expr->token[i].type != JIM_EXPROP_COLON_RIGHT) { + continue; + } + + /* COLON_RIGHT found: get the indexes needed to move the tokens in the stack (if any) */ + if (ExprTernaryGetMoveIndices(expr, i, &prev_right_index, &prev_left_index) == 0) { + continue; + } + + /* + ** rotate tokens down + ** + ** +-> [i] : JIM_EXPROP_COLON_RIGHT + ** | | | + ** | V V + ** | [...] : ... + ** | | | + ** | V V + ** | [...] : ... + ** | | | + ** | V V + ** +- [prev_right_index] : JIM_EXPROP_COLON_RIGHT + */ + tmp = expr->token[prev_right_index]; + for (j = prev_right_index; j < i; j++) { + expr->token[j] = expr->token[j + 1]; + } + expr->token[i] = tmp; + + /* Increment the 'skip' count associated to the previous JIM_EXPROP_COLON_LEFT token + * + * This is 'colon left increment' = i - prev_right_index + * + * [prev_left_index] : JIM_EXPROP_LEFT_RIGHT + * [prev_left_index-1] : skip_count + * + */ + JimWideValue(expr->token[prev_left_index-1].objPtr) += (i - prev_right_index); + + /* Adjust for i-- in the loop */ + i++; + } +} + +static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *fileNameObj) +{ + Jim_Stack stack; + ExprByteCode *expr; + int ok = 1; + int i; + int prevtt = JIM_TT_NONE; + int have_ternary = 0; + + /* -1 for EOL */ + int count = tokenlist->count - 1; + + expr = Jim_Alloc(sizeof(*expr)); + expr->inUse = 1; + expr->len = 0; + + Jim_InitStack(&stack); + + /* Need extra bytecodes for lazy operators. + * Also check for the ternary operator + */ + for (i = 0; i < tokenlist->count; i++) { + ParseToken *t = &tokenlist->list[i]; + + if (JimExprOperatorInfoByOpcode(t->type)->lazy == LAZY_OP) { + count += 2; + /* Ternary is a lazy op but also needs reordering */ + if (t->type == JIM_EXPROP_TERNARY) { + have_ternary = 1; + } + } + } + + expr->token = Jim_Alloc(sizeof(ScriptToken) * count); + + for (i = 0; i < tokenlist->count && ok; i++) { + ParseToken *t = &tokenlist->list[i]; + + /* Next token will be stored here */ + struct ScriptToken *token = &expr->token[expr->len]; + + if (t->type == JIM_TT_EOL) { + break; + } + + switch (t->type) { + case JIM_TT_STR: + case JIM_TT_ESC: + case JIM_TT_VAR: + case JIM_TT_DICTSUGAR: + case JIM_TT_EXPRSUGAR: + case JIM_TT_CMD: + token->objPtr = Jim_NewStringObj(interp, t->token, t->len); + token->type = t->type; + if (t->type == JIM_TT_CMD) { + /* Only commands need source info */ + JimSetSourceInfo(interp, token->objPtr, fileNameObj, t->line); + } + expr->len++; + break; + + case JIM_TT_EXPR_INT: + token->objPtr = Jim_NewIntObj(interp, strtoull(t->token, NULL, 0)); + token->type = t->type; + expr->len++; + break; + + case JIM_TT_EXPR_DOUBLE: + token->objPtr = Jim_NewDoubleObj(interp, strtod(t->token, NULL)); + token->type = t->type; + expr->len++; + break; + + case JIM_TT_SUBEXPR_START: + Jim_StackPush(&stack, t); + prevtt = JIM_TT_NONE; + continue; + + case JIM_TT_SUBEXPR_COMMA: + /* Simple approach. Comma is simply ignored */ + continue; + + case JIM_TT_SUBEXPR_END: + ok = 0; + while (Jim_StackLen(&stack)) { + ParseToken *tt = Jim_StackPop(&stack); + + if (tt->type == JIM_TT_SUBEXPR_START) { + ok = 1; + break; + } + + if (ExprAddOperator(interp, expr, tt) != JIM_OK) { + goto err; + } + } + if (!ok) { + Jim_SetResultString(interp, "Unexpected close parenthesis", -1); + goto err; + } + break; + + + default:{ + /* Must be an operator */ + const struct Jim_ExprOperator *op; + ParseToken *tt; + + /* Convert -/+ to unary minus or unary plus if necessary */ + if (prevtt == JIM_TT_NONE || prevtt >= JIM_TT_EXPR_OP) { + if (t->type == JIM_EXPROP_SUB) { + t->type = JIM_EXPROP_UNARYMINUS; + } + else if (t->type == JIM_EXPROP_ADD) { + t->type = JIM_EXPROP_UNARYPLUS; + } + } + + op = JimExprOperatorInfoByOpcode(t->type); + + /* Now handle precedence */ + while ((tt = Jim_StackPeek(&stack)) != NULL) { + const struct Jim_ExprOperator *tt_op = + JimExprOperatorInfoByOpcode(tt->type); + + /* Note that right-to-left associativity of ?: operator is handled later */ + + if (op->arity != 1 && tt_op->precedence >= op->precedence) { + if (ExprAddOperator(interp, expr, tt) != JIM_OK) { + ok = 0; + goto err; + } + Jim_StackPop(&stack); + } + else { + break; + } + } + Jim_StackPush(&stack, t); + break; + } + } + prevtt = t->type; + } + + /* Reduce any remaining subexpr */ + while (Jim_StackLen(&stack)) { + ParseToken *tt = Jim_StackPop(&stack); + + if (tt->type == JIM_TT_SUBEXPR_START) { + ok = 0; + Jim_SetResultString(interp, "Missing close parenthesis", -1); + goto err; + } + if (ExprAddOperator(interp, expr, tt) != JIM_OK) { + ok = 0; + goto err; + } + } + + if (have_ternary) { + ExprTernaryReorderExpression(interp, expr); + } + + err: + /* Free the stack used for the compilation. */ + Jim_FreeStack(&stack); + + for (i = 0; i < expr->len; i++) { + Jim_IncrRefCount(expr->token[i].objPtr); + } + + if (!ok) { + ExprFreeByteCode(interp, expr); + return NULL; + } + + return expr; +} + + +/* This method takes the string representation of an expression + * and generates a program for the Expr's stack-based VM. */ +static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) +{ + int exprTextLen; + const char *exprText; + struct JimParserCtx parser; + struct ExprByteCode *expr; + ParseTokenList tokenlist; + int line; + Jim_Obj *fileNameObj; + int rc = JIM_ERR; + + /* Try to get information about filename / line number */ + if (objPtr->typePtr == &sourceObjType) { + fileNameObj = objPtr->internalRep.sourceValue.fileNameObj; + line = objPtr->internalRep.sourceValue.lineNumber; + } + else { + fileNameObj = interp->emptyObj; + line = 1; + } + Jim_IncrRefCount(fileNameObj); + + exprText = Jim_GetString(objPtr, &exprTextLen); + + /* Initially tokenise the expression into tokenlist */ + ScriptTokenListInit(&tokenlist); + + JimParserInit(&parser, exprText, exprTextLen, line); + while (!parser.eof) { + if (JimParseExpression(&parser) != JIM_OK) { + ScriptTokenListFree(&tokenlist); + invalidexpr: + Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr); + expr = NULL; + goto err; + } + + ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, + parser.tline); + } + +#ifdef DEBUG_SHOW_EXPR_TOKENS + { + int i; + printf("==== Expr Tokens ====\n"); + for (i = 0; i < tokenlist.count; i++) { + printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type), + tokenlist.list[i].len, tokenlist.list[i].token); + } + } +#endif + + /* Now create the expression bytecode from the tokenlist */ + expr = ExprCreateByteCode(interp, &tokenlist, fileNameObj); + + /* No longer need the token list */ + ScriptTokenListFree(&tokenlist); + + if (!expr) { + goto err; + } + +#ifdef DEBUG_SHOW_EXPR + { + int i; + + printf("==== Expr ====\n"); + for (i = 0; i < expr->len; i++) { + ScriptToken *t = &expr->token[i]; + + printf("[%2d] %s '%s'\n", i, jim_tt_name(t->type), Jim_String(t->objPtr)); + } + } +#endif + + /* Check program correctness. */ + if (ExprCheckCorrectness(expr) != JIM_OK) { + ExprFreeByteCode(interp, expr); + goto invalidexpr; + } + + rc = JIM_OK; + + err: + /* Free the old internal rep and set the new one. */ + Jim_DecrRefCount(interp, fileNameObj); + Jim_FreeIntRep(interp, objPtr); + Jim_SetIntRepPtr(objPtr, expr); + objPtr->typePtr = &exprObjType; + return rc; +} + +static ExprByteCode *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr) +{ + if (objPtr->typePtr != &exprObjType) { + if (SetExprFromAny(interp, objPtr) != JIM_OK) { + return NULL; + } + } + return (ExprByteCode *) Jim_GetIntRepPtr(objPtr); +} + +/* ----------------------------------------------------------------------------- + * Expressions evaluation. + * Jim uses a specialized stack-based virtual machine for expressions, + * that takes advantage of the fact that expr's operators + * can't be redefined. + * + * Jim_EvalExpression() uses the bytecode compiled by + * SetExprFromAny() method of the "expression" object. + * + * On success a Tcl Object containing the result of the evaluation + * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is + * returned. + * On error the function returns a retcode != to JIM_OK and set a suitable + * error on the interp. + * ---------------------------------------------------------------------------*/ +#define JIM_EE_STATICSTACK_LEN 10 + +int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr) +{ + ExprByteCode *expr; + Jim_Obj *staticStack[JIM_EE_STATICSTACK_LEN]; + int i; + int retcode = JIM_OK; + struct JimExprState e; + + expr = JimGetExpression(interp, exprObjPtr); + if (!expr) { + return JIM_ERR; /* error in expression. */ + } + +#ifdef JIM_OPTIMIZATION + /* Check for one of the following common expressions used by while/for + * + * CONST + * $a + * !$a + * $a < CONST, $a < $b + * $a <= CONST, $a <= $b + * $a > CONST, $a > $b + * $a >= CONST, $a >= $b + * $a != CONST, $a != $b + * $a == CONST, $a == $b + */ + { + Jim_Obj *objPtr; + + /* STEP 1 -- Check if there are the conditions to run the specialized + * version of while */ + + switch (expr->len) { + case 1: + if (expr->token[0].type == JIM_TT_EXPR_INT) { + *exprResultPtrPtr = expr->token[0].objPtr; + Jim_IncrRefCount(*exprResultPtrPtr); + return JIM_OK; + } + if (expr->token[0].type == JIM_TT_VAR) { + objPtr = Jim_GetVariable(interp, expr->token[0].objPtr, JIM_ERRMSG); + if (objPtr) { + *exprResultPtrPtr = objPtr; + Jim_IncrRefCount(*exprResultPtrPtr); + return JIM_OK; + } + } + break; + + case 2: + if (expr->token[1].type == JIM_EXPROP_NOT && expr->token[0].type == JIM_TT_VAR) { + jim_wide wideValue; + + objPtr = Jim_GetVariable(interp, expr->token[0].objPtr, JIM_NONE); + if (objPtr && JimIsWide(objPtr) + && Jim_GetWide(interp, objPtr, &wideValue) == JIM_OK) { + *exprResultPtrPtr = wideValue ? interp->falseObj : interp->trueObj; + Jim_IncrRefCount(*exprResultPtrPtr); + return JIM_OK; + } + } + break; + + case 3: + if (expr->token[0].type == JIM_TT_VAR && (expr->token[1].type == JIM_TT_EXPR_INT + || expr->token[1].type == JIM_TT_VAR)) { + switch (expr->token[2].type) { + case JIM_EXPROP_LT: + case JIM_EXPROP_LTE: + case JIM_EXPROP_GT: + case JIM_EXPROP_GTE: + case JIM_EXPROP_NUMEQ: + case JIM_EXPROP_NUMNE:{ + /* optimise ok */ + jim_wide wideValueA; + jim_wide wideValueB; + + objPtr = Jim_GetVariable(interp, expr->token[0].objPtr, JIM_NONE); + if (objPtr && JimIsWide(objPtr) + && Jim_GetWide(interp, objPtr, &wideValueA) == JIM_OK) { + if (expr->token[1].type == JIM_TT_VAR) { + objPtr = + Jim_GetVariable(interp, expr->token[1].objPtr, + JIM_NONE); + } + else { + objPtr = expr->token[1].objPtr; + } + if (objPtr && JimIsWide(objPtr) + && Jim_GetWide(interp, objPtr, &wideValueB) == JIM_OK) { + int cmpRes; + + switch (expr->token[2].type) { + case JIM_EXPROP_LT: + cmpRes = wideValueA < wideValueB; + break; + case JIM_EXPROP_LTE: + cmpRes = wideValueA <= wideValueB; + break; + case JIM_EXPROP_GT: + cmpRes = wideValueA > wideValueB; + break; + case JIM_EXPROP_GTE: + cmpRes = wideValueA >= wideValueB; + break; + case JIM_EXPROP_NUMEQ: + cmpRes = wideValueA == wideValueB; + break; + case JIM_EXPROP_NUMNE: + cmpRes = wideValueA != wideValueB; + break; + default: /*notreached */ + cmpRes = 0; + } + *exprResultPtrPtr = + cmpRes ? interp->trueObj : interp->falseObj; + Jim_IncrRefCount(*exprResultPtrPtr); + return JIM_OK; + } + } + } + } + } + break; + } + } +#endif + + /* In order to avoid that the internal repr gets freed due to + * shimmering of the exprObjPtr's object, we make the internal rep + * shared. */ + expr->inUse++; + + /* The stack-based expr VM itself */ + + /* Stack allocation. Expr programs have the feature that + * a program of length N can't require a stack longer than + * N. */ + if (expr->len > JIM_EE_STATICSTACK_LEN) + e.stack = Jim_Alloc(sizeof(Jim_Obj *) * expr->len); + else + e.stack = staticStack; + + e.stacklen = 0; + + /* Execute every instruction */ + for (i = 0; i < expr->len && retcode == JIM_OK; i++) { + Jim_Obj *objPtr; + + switch (expr->token[i].type) { + case JIM_TT_EXPR_INT: + case JIM_TT_EXPR_DOUBLE: + case JIM_TT_STR: + ExprPush(&e, expr->token[i].objPtr); + break; + + case JIM_TT_VAR: + objPtr = Jim_GetVariable(interp, expr->token[i].objPtr, JIM_ERRMSG); + if (objPtr) { + ExprPush(&e, objPtr); + } + else { + retcode = JIM_ERR; + } + break; + + case JIM_TT_DICTSUGAR: + objPtr = JimExpandDictSugar(interp, expr->token[i].objPtr); + if (objPtr) { + ExprPush(&e, objPtr); + } + else { + retcode = JIM_ERR; + } + break; + + case JIM_TT_ESC: + retcode = Jim_SubstObj(interp, expr->token[i].objPtr, &objPtr, JIM_NONE); + if (retcode == JIM_OK) { + ExprPush(&e, objPtr); + } + break; + + case JIM_TT_CMD: + retcode = Jim_EvalObj(interp, expr->token[i].objPtr); + if (retcode == JIM_OK) { + ExprPush(&e, Jim_GetResult(interp)); + } + break; + + default:{ + /* Find and execute the operation */ + e.skip = 0; + e.opcode = expr->token[i].type; + + retcode = JimExprOperatorInfoByOpcode(e.opcode)->funcop(interp, &e); + /* Skip some opcodes if necessary */ + i += e.skip; + continue; + } + } + } + + expr->inUse--; + + if (retcode == JIM_OK) { + *exprResultPtrPtr = ExprPop(&e); + } + else { + for (i = 0; i < e.stacklen; i++) { + Jim_DecrRefCount(interp, e.stack[i]); + } + } + if (e.stack != staticStack) { + Jim_Free(e.stack); + } + return retcode; +} + +int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr) +{ + int retcode; + jim_wide wideValue; + double doubleValue; + Jim_Obj *exprResultPtr; + + retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr); + if (retcode != JIM_OK) + return retcode; + + if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) { + if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK) { + Jim_DecrRefCount(interp, exprResultPtr); + return JIM_ERR; + } + else { + Jim_DecrRefCount(interp, exprResultPtr); + *boolPtr = doubleValue != 0; + return JIM_OK; + } + } + *boolPtr = wideValue != 0; + + Jim_DecrRefCount(interp, exprResultPtr); + return JIM_OK; +} + +/* ----------------------------------------------------------------------------- + * ScanFormat String Object + * ---------------------------------------------------------------------------*/ + +/* This Jim_Obj will held a parsed representation of a format string passed to + * the Jim_ScanString command. For error diagnostics, the scanformat string has + * to be parsed in its entirely first and then, if correct, can be used for + * scanning. To avoid endless re-parsing, the parsed representation will be + * stored in an internal representation and re-used for performance reason. */ + +/* A ScanFmtPartDescr will held the information of /one/ part of the whole + * scanformat string. This part will later be used to extract information + * out from the string to be parsed by Jim_ScanString */ + +typedef struct ScanFmtPartDescr +{ + char type; /* Type of conversion (e.g. c, d, f) */ + char modifier; /* Modify type (e.g. l - long, h - short */ + size_t width; /* Maximal width of input to be converted */ + int pos; /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */ + char *arg; /* Specification of a CHARSET conversion */ + char *prefix; /* Prefix to be scanned literally before conversion */ +} ScanFmtPartDescr; + +/* The ScanFmtStringObj will hold the internal representation of a scanformat + * string parsed and separated in part descriptions. Furthermore it contains + * the original string representation of the scanformat string to allow for + * fast update of the Jim_Obj's string representation part. + * + * As an add-on the internal object representation adds some scratch pad area + * for usage by Jim_ScanString to avoid endless allocating and freeing of + * memory for purpose of string scanning. + * + * The error member points to a static allocated string in case of a mal- + * formed scanformat string or it contains '0' (NULL) in case of a valid + * parse representation. + * + * The whole memory of the internal representation is allocated as a single + * area of memory that will be internally separated. So freeing and duplicating + * of such an object is cheap */ + +typedef struct ScanFmtStringObj +{ + jim_wide size; /* Size of internal repr in bytes */ + char *stringRep; /* Original string representation */ + size_t count; /* Number of ScanFmtPartDescr contained */ + size_t convCount; /* Number of conversions that will assign */ + size_t maxPos; /* Max position index if XPG3 is used */ + const char *error; /* Ptr to error text (NULL if no error */ + char *scratch; /* Some scratch pad used by Jim_ScanString */ + ScanFmtPartDescr descr[1]; /* The vector of partial descriptions */ +} ScanFmtStringObj; + + +static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); +static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); +static void UpdateStringOfScanFmt(Jim_Obj *objPtr); + +static const Jim_ObjType scanFmtStringObjType = { + "scanformatstring", + FreeScanFmtInternalRep, + DupScanFmtInternalRep, + UpdateStringOfScanFmt, + JIM_TYPE_NONE, +}; + +void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) +{ + JIM_NOTUSED(interp); + Jim_Free((char *)objPtr->internalRep.ptr); + objPtr->internalRep.ptr = 0; +} + +void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) +{ + size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size; + ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size); + + JIM_NOTUSED(interp); + memcpy(newVec, srcPtr->internalRep.ptr, size); + dupPtr->internalRep.ptr = newVec; + dupPtr->typePtr = &scanFmtStringObjType; +} + +void UpdateStringOfScanFmt(Jim_Obj *objPtr) +{ + char *bytes = ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep; + + objPtr->bytes = Jim_StrDup(bytes); + objPtr->length = strlen(bytes); +} + +/* SetScanFmtFromAny will parse a given string and create the internal + * representation of the format specification. In case of an error + * the error data member of the internal representation will be set + * to an descriptive error text and the function will be left with + * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat + * specification */ + +static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr) +{ + ScanFmtStringObj *fmtObj; + char *buffer; + int maxCount, i, approxSize, lastPos = -1; + const char *fmt = objPtr->bytes; + int maxFmtLen = objPtr->length; + const char *fmtEnd = fmt + maxFmtLen; + int curr; + + Jim_FreeIntRep(interp, objPtr); + /* Count how many conversions could take place maximally */ + for (i = 0, maxCount = 0; i < maxFmtLen; ++i) + if (fmt[i] == '%') + ++maxCount; + /* Calculate an approximation of the memory necessary */ + approxSize = sizeof(ScanFmtStringObj) /* Size of the container */ + +(maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */ + +maxFmtLen * sizeof(char) + 3 + 1 /* Scratch + "%n" + '\0' */ + + maxFmtLen * sizeof(char) + 1 /* Original stringrep */ + + maxFmtLen * sizeof(char) /* Arg for CHARSETs */ + +(maxCount + 1) * sizeof(char) /* '\0' for every partial */ + +1; /* safety byte */ + fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize); + memset(fmtObj, 0, approxSize); + fmtObj->size = approxSize; + fmtObj->maxPos = 0; + fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1]; + fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1; + memcpy(fmtObj->stringRep, fmt, maxFmtLen); + buffer = fmtObj->stringRep + maxFmtLen + 1; + objPtr->internalRep.ptr = fmtObj; + objPtr->typePtr = &scanFmtStringObjType; + for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) { + int width = 0, skip; + ScanFmtPartDescr *descr = &fmtObj->descr[curr]; + + fmtObj->count++; + descr->width = 0; /* Assume width unspecified */ + /* Overread and store any "literal" prefix */ + if (*fmt != '%' || fmt[1] == '%') { + descr->type = 0; + descr->prefix = &buffer[i]; + for (; fmt < fmtEnd; ++fmt) { + if (*fmt == '%') { + if (fmt[1] != '%') + break; + ++fmt; + } + buffer[i++] = *fmt; + } + buffer[i++] = 0; + } + /* Skip the conversion introducing '%' sign */ + ++fmt; + /* End reached due to non-conversion literal only? */ + if (fmt >= fmtEnd) + goto done; + descr->pos = 0; /* Assume "natural" positioning */ + if (*fmt == '*') { + descr->pos = -1; /* Okay, conversion will not be assigned */ + ++fmt; + } + else + fmtObj->convCount++; /* Otherwise count as assign-conversion */ + /* Check if next token is a number (could be width or pos */ + if (sscanf(fmt, "%d%n", &width, &skip) == 1) { + fmt += skip; + /* Was the number a XPG3 position specifier? */ + if (descr->pos != -1 && *fmt == '$') { + int prev; + + ++fmt; + descr->pos = width; + width = 0; + /* Look if "natural" postioning and XPG3 one was mixed */ + if ((lastPos == 0 && descr->pos > 0) + || (lastPos > 0 && descr->pos == 0)) { + fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers"; + return JIM_ERR; + } + /* Look if this position was already used */ + for (prev = 0; prev < curr; ++prev) { + if (fmtObj->descr[prev].pos == -1) + continue; + if (fmtObj->descr[prev].pos == descr->pos) { + fmtObj->error = + "variable is assigned by multiple \"%n$\" conversion specifiers"; + return JIM_ERR; + } + } + /* Try to find a width after the XPG3 specifier */ + if (sscanf(fmt, "%d%n", &width, &skip) == 1) { + descr->width = width; + fmt += skip; + } + if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos) + fmtObj->maxPos = descr->pos; + } + else { + /* Number was not a XPG3, so it has to be a width */ + descr->width = width; + } + } + /* If positioning mode was undetermined yet, fix this */ + if (lastPos == -1) + lastPos = descr->pos; + /* Handle CHARSET conversion type ... */ + if (*fmt == '[') { + int swapped = 1, beg = i, end, j; + + descr->type = '['; + descr->arg = &buffer[i]; + ++fmt; + if (*fmt == '^') + buffer[i++] = *fmt++; + if (*fmt == ']') + buffer[i++] = *fmt++; + while (*fmt && *fmt != ']') + buffer[i++] = *fmt++; + if (*fmt != ']') { + fmtObj->error = "unmatched [ in format string"; + return JIM_ERR; + } + end = i; + buffer[i++] = 0; + /* In case a range fence was given "backwards", swap it */ + while (swapped) { + swapped = 0; + for (j = beg + 1; j < end - 1; ++j) { + if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) { + char tmp = buffer[j - 1]; + + buffer[j - 1] = buffer[j + 1]; + buffer[j + 1] = tmp; + swapped = 1; + } + } + } + } + else { + /* Remember any valid modifier if given */ + if (strchr("hlL", *fmt) != 0) + descr->modifier = tolower((int)*fmt++); + + descr->type = *fmt; + if (strchr("efgcsndoxui", *fmt) == 0) { + fmtObj->error = "bad scan conversion character"; + return JIM_ERR; + } + else if (*fmt == 'c' && descr->width != 0) { + fmtObj->error = "field width may not be specified in %c " "conversion"; + return JIM_ERR; + } + else if (*fmt == 'u' && descr->modifier == 'l') { + fmtObj->error = "unsigned wide not supported"; + return JIM_ERR; + } + } + curr++; + } + done: + return JIM_OK; +} + +/* Some accessor macros to allow lowlevel access to fields of internal repr */ + +#define FormatGetCnvCount(_fo_) \ + ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount +#define FormatGetMaxPos(_fo_) \ + ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos +#define FormatGetError(_fo_) \ + ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error + +/* JimScanAString is used to scan an unspecified string that ends with + * next WS, or a string that is specified via a charset. + * + */ +static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str) +{ + char *buffer = Jim_StrDup(str); + char *p = buffer; + + while (*str) { + int c; + int n; + + if (!sdescr && isspace(UCHAR(*str))) + break; /* EOS via WS if unspecified */ + + n = utf8_tounicode(str, &c); + if (sdescr && !JimCharsetMatch(sdescr, c, JIM_CHARSET_SCAN)) + break; + while (n--) + *p++ = *str++; + } + *p = 0; + return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer); +} + +/* ScanOneEntry will scan one entry out of the string passed as argument. + * It use the sscanf() function for this task. After extracting and + * converting of the value, the count of scanned characters will be + * returned of -1 in case of no conversion tool place and string was + * already scanned thru */ + +static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int strLen, + ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr) +{ + const char *tok; + const ScanFmtPartDescr *descr = &fmtObj->descr[idx]; + size_t scanned = 0; + size_t anchor = pos; + int i; + Jim_Obj *tmpObj = NULL; + + /* First pessimistically assume, we will not scan anything :-) */ + *valObjPtr = 0; + if (descr->prefix) { + /* There was a prefix given before the conversion, skip it and adjust + * the string-to-be-parsed accordingly */ + /* XXX: Should be checking strLen, not str[pos] */ + for (i = 0; pos < strLen && descr->prefix[i]; ++i) { + /* If prefix require, skip WS */ + if (isspace(UCHAR(descr->prefix[i]))) + while (pos < strLen && isspace(UCHAR(str[pos]))) + ++pos; + else if (descr->prefix[i] != str[pos]) + break; /* Prefix do not match here, leave the loop */ + else + ++pos; /* Prefix matched so far, next round */ + } + if (pos >= strLen) { + return -1; /* All of str consumed: EOF condition */ + } + else if (descr->prefix[i] != 0) + return 0; /* Not whole prefix consumed, no conversion possible */ + } + /* For all but following conversion, skip leading WS */ + if (descr->type != 'c' && descr->type != '[' && descr->type != 'n') + while (isspace(UCHAR(str[pos]))) + ++pos; + /* Determine how much skipped/scanned so far */ + scanned = pos - anchor; + + /* %c is a special, simple case. no width */ + if (descr->type == 'n') { + /* Return pseudo conversion means: how much scanned so far? */ + *valObjPtr = Jim_NewIntObj(interp, anchor + scanned); + } + else if (pos >= strLen) { + /* Cannot scan anything, as str is totally consumed */ + return -1; + } + else if (descr->type == 'c') { + int c; + scanned += utf8_tounicode(&str[pos], &c); + *valObjPtr = Jim_NewIntObj(interp, c); + return scanned; + } + else { + /* Processing of conversions follows ... */ + if (descr->width > 0) { + /* Do not try to scan as fas as possible but only the given width. + * To ensure this, we copy the part that should be scanned. */ + size_t sLen = utf8_strlen(&str[pos], strLen - pos); + size_t tLen = descr->width > sLen ? sLen : descr->width; + + tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen); + tok = tmpObj->bytes; + } + else { + /* As no width was given, simply refer to the original string */ + tok = &str[pos]; + } + switch (descr->type) { + case 'd': + case 'o': + case 'x': + case 'u': + case 'i':{ + char *endp; /* Position where the number finished */ + jim_wide w; + + int base = descr->type == 'o' ? 8 + : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10; + + /* Try to scan a number with the given base */ + w = strtoull(tok, &endp, base); + if (endp == tok && base == 0) { + /* If scanning failed, and base was undetermined, simply + * put it to 10 and try once more. This should catch the + * case where %i begin to parse a number prefix (e.g. + * '0x' but no further digits follows. This will be + * handled as a ZERO followed by a char 'x' by Tcl */ + w = strtoull(tok, &endp, 10); + } + + if (endp != tok) { + /* There was some number sucessfully scanned! */ + *valObjPtr = Jim_NewIntObj(interp, w); + + /* Adjust the number-of-chars scanned so far */ + scanned += endp - tok; + } + else { + /* Nothing was scanned. We have to determine if this + * happened due to e.g. prefix mismatch or input str + * exhausted */ + scanned = *tok ? 0 : -1; + } + break; + } + case 's': + case '[':{ + *valObjPtr = JimScanAString(interp, descr->arg, tok); + scanned += Jim_Length(*valObjPtr); + break; + } + case 'e': + case 'f': + case 'g':{ + char *endp; + double value = strtod(tok, &endp); + + if (endp != tok) { + /* There was some number sucessfully scanned! */ + *valObjPtr = Jim_NewDoubleObj(interp, value); + /* Adjust the number-of-chars scanned so far */ + scanned += endp - tok; + } + else { + /* Nothing was scanned. We have to determine if this + * happened due to e.g. prefix mismatch or input str + * exhausted */ + scanned = *tok ? 0 : -1; + } + break; + } + } + /* If a substring was allocated (due to pre-defined width) do not + * forget to free it */ + if (tmpObj) { + Jim_FreeNewObj(interp, tmpObj); + } + } + return scanned; +} + +/* Jim_ScanString is the workhorse of string scanning. It will scan a given + * string and returns all converted (and not ignored) values in a list back + * to the caller. If an error occured, a NULL pointer will be returned */ + +Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags) +{ + size_t i, pos; + int scanned = 1; + const char *str = Jim_String(strObjPtr); + int strLen = Jim_Utf8Length(interp, strObjPtr); + Jim_Obj *resultList = 0; + Jim_Obj **resultVec = 0; + int resultc; + Jim_Obj *emptyStr = 0; + ScanFmtStringObj *fmtObj; + + /* This should never happen. The format object should already be of the correct type */ + JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format")); + + fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr; + /* Check if format specification was valid */ + if (fmtObj->error != 0) { + if (flags & JIM_ERRMSG) + Jim_SetResultString(interp, fmtObj->error, -1); + return 0; + } + /* Allocate a new "shared" empty string for all unassigned conversions */ + emptyStr = Jim_NewEmptyStringObj(interp); + Jim_IncrRefCount(emptyStr); + /* Create a list and fill it with empty strings up to max specified XPG3 */ + resultList = Jim_NewListObj(interp, 0, 0); + if (fmtObj->maxPos > 0) { + for (i = 0; i < fmtObj->maxPos; ++i) + Jim_ListAppendElement(interp, resultList, emptyStr); + JimListGetElements(interp, resultList, &resultc, &resultVec); + } + /* Now handle every partial format description */ + for (i = 0, pos = 0; i < fmtObj->count; ++i) { + ScanFmtPartDescr *descr = &(fmtObj->descr[i]); + Jim_Obj *value = 0; + + /* Only last type may be "literal" w/o conversion - skip it! */ + if (descr->type == 0) + continue; + /* As long as any conversion could be done, we will proceed */ + if (scanned > 0) + scanned = ScanOneEntry(interp, str, pos, strLen, fmtObj, i, &value); + /* In case our first try results in EOF, we will leave */ + if (scanned == -1 && i == 0) + goto eof; + /* Advance next pos-to-be-scanned for the amount scanned already */ + pos += scanned; + + /* value == 0 means no conversion took place so take empty string */ + if (value == 0) + value = Jim_NewEmptyStringObj(interp); + /* If value is a non-assignable one, skip it */ + if (descr->pos == -1) { + Jim_FreeNewObj(interp, value); + } + else if (descr->pos == 0) + /* Otherwise append it to the result list if no XPG3 was given */ + Jim_ListAppendElement(interp, resultList, value); + else if (resultVec[descr->pos - 1] == emptyStr) { + /* But due to given XPG3, put the value into the corr. slot */ + Jim_DecrRefCount(interp, resultVec[descr->pos - 1]); + Jim_IncrRefCount(value); + resultVec[descr->pos - 1] = value; + } + else { + /* Otherwise, the slot was already used - free obj and ERROR */ + Jim_FreeNewObj(interp, value); + goto err; + } + } + Jim_DecrRefCount(interp, emptyStr); + return resultList; + eof: + Jim_DecrRefCount(interp, emptyStr); + Jim_FreeNewObj(interp, resultList); + return (Jim_Obj *)EOF; + err: + Jim_DecrRefCount(interp, emptyStr); + Jim_FreeNewObj(interp, resultList); + return 0; +} + +/* ----------------------------------------------------------------------------- + * Pseudo Random Number Generation + * ---------------------------------------------------------------------------*/ +/* Initialize the sbox with the numbers from 0 to 255 */ +static void JimPrngInit(Jim_Interp *interp) +{ +#define PRNG_SEED_SIZE 256 + int i; + unsigned int *seed; + time_t t = time(NULL); + + interp->prngState = Jim_Alloc(sizeof(Jim_PrngState)); + + seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed)); + for (i = 0; i < PRNG_SEED_SIZE; i++) { + seed[i] = (rand() ^ t ^ clock()); + } + JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed)); + Jim_Free(seed); +} + +/* Generates N bytes of random data */ +static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len) +{ + Jim_PrngState *prng; + unsigned char *destByte = (unsigned char *)dest; + unsigned int si, sj, x; + + /* initialization, only needed the first time */ + if (interp->prngState == NULL) + JimPrngInit(interp); + prng = interp->prngState; + /* generates 'len' bytes of pseudo-random numbers */ + for (x = 0; x < len; x++) { + prng->i = (prng->i + 1) & 0xff; + si = prng->sbox[prng->i]; + prng->j = (prng->j + si) & 0xff; + sj = prng->sbox[prng->j]; + prng->sbox[prng->i] = sj; + prng->sbox[prng->j] = si; + *destByte++ = prng->sbox[(si + sj) & 0xff]; + } +} + +/* Re-seed the generator with user-provided bytes */ +static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen) +{ + int i; + Jim_PrngState *prng; + + /* initialization, only needed the first time */ + if (interp->prngState == NULL) + JimPrngInit(interp); + prng = interp->prngState; + + /* Set the sbox[i] with i */ + for (i = 0; i < 256; i++) + prng->sbox[i] = i; + /* Now use the seed to perform a random permutation of the sbox */ + for (i = 0; i < seedLen; i++) { + unsigned char t; + + t = prng->sbox[i & 0xFF]; + prng->sbox[i & 0xFF] = prng->sbox[seed[i]]; + prng->sbox[seed[i]] = t; + } + prng->i = prng->j = 0; + + /* discard at least the first 256 bytes of stream. + * borrow the seed buffer for this + */ + for (i = 0; i < 256; i += seedLen) { + JimRandomBytes(interp, seed, seedLen); + } +} + +/* [incr] */ +static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + jim_wide wideValue, increment = 1; + Jim_Obj *intObjPtr; + + if (argc != 2 && argc != 3) { + Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?"); + return JIM_ERR; + } + if (argc == 3) { + if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK) + return JIM_ERR; + } + intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED); + if (!intObjPtr) { + /* Set missing variable to 0 */ + wideValue = 0; + } + else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) { + return JIM_ERR; + } + if (!intObjPtr || Jim_IsShared(intObjPtr)) { + intObjPtr = Jim_NewIntObj(interp, wideValue + increment); + if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) { + Jim_FreeNewObj(interp, intObjPtr); + return JIM_ERR; + } + } + else { + /* Can do it the quick way */ + Jim_InvalidateStringRep(intObjPtr); + JimWideValue(intObjPtr) = wideValue + increment; + + /* The following step is required in order to invalidate the + * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */ + if (argv[1]->typePtr != &variableObjType) { + /* Note that this can't fail since GetVariable already succeeded */ + Jim_SetVariable(interp, argv[1], intObjPtr); + } + } + Jim_SetResult(interp, intObjPtr); + return JIM_OK; +} + + +/* ----------------------------------------------------------------------------- + * Eval + * ---------------------------------------------------------------------------*/ +#define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */ +#define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */ + +/* Handle calls to the [unknown] command */ +static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv, Jim_Obj *fileNameObj, + int linenr) +{ + Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN]; + int retCode; + + /* If JimUnknown() is recursively called too many times... + * done here + */ + if (interp->unknown_called > 50) { + return JIM_ERR; + } + + /* If the [unknown] command does not exists returns + * just now */ + if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL) + return JIM_ERR; + + /* The object interp->unknown just contains + * the "unknown" string, it is used in order to + * avoid to lookup the unknown command every time + * but instread to cache the result. */ + if (argc + 1 <= JIM_EVAL_SARGV_LEN) + v = sv; + else + v = Jim_Alloc(sizeof(Jim_Obj *) * (argc + 1)); + /* Make a copy of the arguments vector, but shifted on + * the right of one position. The command name of the + * command will be instead the first argument of the + * [unknown] call. */ + memcpy(v + 1, argv, sizeof(Jim_Obj *) * argc); + v[0] = interp->unknown; + /* Call it */ + interp->unknown_called++; + retCode = JimEvalObjVector(interp, argc + 1, v, fileNameObj, linenr); + interp->unknown_called--; + + /* Clean up */ + if (v != sv) + Jim_Free(v); + return retCode; +} + +/* Eval the object vector 'objv' composed of 'objc' elements. + * Every element is used as single argument. + * Jim_EvalObj() will call this function every time its object + * argument is of "list" type, with no string representation. + * + * This is possible because the string representation of a + * list object generated by the UpdateStringOfList is made + * in a way that ensures that every list element is a different + * command argument. */ +static int JimEvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv, + Jim_Obj *fileNameObj, int linenr) +{ + int i, retcode; + Jim_Cmd *cmdPtr; + + /* Incr refcount of arguments. */ + for (i = 0; i < objc; i++) + Jim_IncrRefCount(objv[i]); + /* Command lookup */ + cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG); + if (cmdPtr == NULL) { + retcode = JimUnknown(interp, objc, objv, fileNameObj, linenr); + } + else { + /* Call it -- Make sure result is an empty object. */ + JimIncrCmdRefCount(cmdPtr); + Jim_SetEmptyResult(interp); + if (cmdPtr->isproc) { + retcode = JimCallProcedure(interp, cmdPtr, fileNameObj, linenr, objc, objv); + } + else { + interp->cmdPrivData = cmdPtr->u.native.privData; + retcode = cmdPtr->u.native.cmdProc(interp, objc, objv); + } + JimDecrCmdRefCount(interp, cmdPtr); + } + /* Decr refcount of arguments and return the retcode */ + for (i = 0; i < objc; i++) + Jim_DecrRefCount(interp, objv[i]); + + return retcode; +} + +int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv) +{ + return JimEvalObjVector(interp, objc, objv, interp->emptyObj, 1); +} + +/** + * Invokes 'prefix' as a command with the objv array as arguments. + */ +int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv) +{ + int i; + int ret; + Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv)); + + nargv[0] = prefix; + for (i = 0; i < objc; i++) { + nargv[i + 1] = objv[i]; + } + ret = Jim_EvalObjVector(interp, objc + 1, nargv); + Jim_Free(nargv); + return ret; +} + +static void JimAddErrorToStack(Jim_Interp *interp, int retcode, Jim_Obj *fileNameObj, int line) +{ + int rc = retcode; + + if (rc == JIM_ERR && !interp->errorFlag) { + /* This is the first error, so save the file/line information and reset the stack */ + interp->errorFlag = 1; + Jim_IncrRefCount(fileNameObj); + Jim_DecrRefCount(interp, interp->errorFileNameObj); + interp->errorFileNameObj = fileNameObj; + interp->errorLine = line; + + JimResetStackTrace(interp); + /* Always add a level where the error first occurs */ + interp->addStackTrace++; + } + + /* Now if this is an "interesting" level, add it to the stack trace */ + if (rc == JIM_ERR && interp->addStackTrace > 0) { + /* Add the stack info for the current level */ + + JimAppendStackTrace(interp, Jim_String(interp->errorProc), fileNameObj, line); + + /* Note: if we didn't have a filename for this level, + * don't clear the addStackTrace flag + * so we can pick it up at the next level + */ + if (Jim_Length(fileNameObj)) { + interp->addStackTrace = 0; + } + + Jim_DecrRefCount(interp, interp->errorProc); + interp->errorProc = interp->emptyObj; + Jim_IncrRefCount(interp->errorProc); + } + else if (rc == JIM_RETURN && interp->returnCode == JIM_ERR) { + /* Propagate the addStackTrace value through 'return -code error' */ + } + else { + interp->addStackTrace = 0; + } +} + +/* And delete any local procs */ +static void JimDeleteLocalProcs(Jim_Interp *interp) +{ + if (interp->localProcs) { + char *procname; + + while ((procname = Jim_StackPop(interp->localProcs)) != NULL) { + /* If there is a pushed command, find it */ + Jim_Cmd *prevCmd = NULL; + Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, procname); + if (he) { + Jim_Cmd *cmd = (Jim_Cmd *)he->u.val; + if (cmd->isproc && cmd->u.proc.prevCmd) { + prevCmd = cmd->u.proc.prevCmd; + cmd->u.proc.prevCmd = NULL; + } + } + + /* Delete the local proc */ + Jim_DeleteCommand(interp, procname); + + if (prevCmd) { + /* And restore the pushed command */ + Jim_AddHashEntry(&interp->commands, procname, prevCmd); + } + Jim_Free(procname); + } + Jim_FreeStack(interp->localProcs); + Jim_Free(interp->localProcs); + interp->localProcs = NULL; + } +} + +static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr) +{ + Jim_Obj *objPtr; + + switch (token->type) { + case JIM_TT_STR: + case JIM_TT_ESC: + objPtr = token->objPtr; + break; + case JIM_TT_VAR: + objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG); + break; + case JIM_TT_DICTSUGAR: + objPtr = JimExpandDictSugar(interp, token->objPtr); + break; + case JIM_TT_EXPRSUGAR: + objPtr = JimExpandExprSugar(interp, token->objPtr); + break; + case JIM_TT_CMD: + switch (Jim_EvalObj(interp, token->objPtr)) { + case JIM_OK: + case JIM_RETURN: + objPtr = interp->result; + break; + case JIM_BREAK: + /* Stop substituting */ + return JIM_BREAK; + case JIM_CONTINUE: + /* just skip this one */ + return JIM_CONTINUE; + default: + return JIM_ERR; + } + break; + default: + JimPanic((1, + "default token type (%d) reached " "in Jim_SubstObj().", token->type)); + objPtr = NULL; + break; + } + if (objPtr) { + *objPtrPtr = objPtr; + return JIM_OK; + } + return JIM_ERR; +} + +/* Interpolate the given tokens into a unique Jim_Obj returned by reference + * via *objPtrPtr. This function is only called by Jim_EvalObj() and Jim_SubstObj() + * The returned object has refcount = 0. + */ +static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags) +{ + int totlen = 0, i; + Jim_Obj **intv; + Jim_Obj *sintv[JIM_EVAL_SINTV_LEN]; + Jim_Obj *objPtr; + char *s; + + if (tokens <= JIM_EVAL_SINTV_LEN) + intv = sintv; + else + intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens); + + /* Compute every token forming the argument + * in the intv objects vector. */ + for (i = 0; i < tokens; i++) { + switch (JimSubstOneToken(interp, &token[i], &intv[i])) { + case JIM_OK: + case JIM_RETURN: + break; + case JIM_BREAK: + if (flags & JIM_SUBST_FLAG) { + /* Stop here */ + tokens = i; + continue; + } + /* XXX: Should probably set an error about break outside loop */ + /* fall through to error */ + case JIM_CONTINUE: + if (flags & JIM_SUBST_FLAG) { + intv[i] = NULL; + continue; + } + /* XXX: Ditto continue outside loop */ + /* fall through to error */ + default: + while (i--) { + Jim_DecrRefCount(interp, intv[i]); + } + if (intv != sintv) { + Jim_Free(intv); + } + return NULL; + } + Jim_IncrRefCount(intv[i]); + Jim_String(intv[i]); + totlen += intv[i]->length; + } + + /* Fast path return for a single token */ + if (tokens == 1 && intv[0] && intv == sintv) { + Jim_DecrRefCount(interp, intv[0]); + return intv[0]; + } + + /* Concatenate every token in an unique + * object. */ + objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0); + + if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC + && token[2].type == JIM_TT_VAR) { + /* May be able to do fast interpolated object -> dictSubst */ + objPtr->typePtr = &interpolatedObjType; + objPtr->internalRep.twoPtrValue.ptr1 = (void *)token; + objPtr->internalRep.twoPtrValue.ptr2 = intv[2]; + Jim_IncrRefCount(intv[2]); + } + + s = objPtr->bytes = Jim_Alloc(totlen + 1); + objPtr->length = totlen; + for (i = 0; i < tokens; i++) { + if (intv[i]) { + memcpy(s, intv[i]->bytes, intv[i]->length); + s += intv[i]->length; + Jim_DecrRefCount(interp, intv[i]); + } + } + objPtr->bytes[totlen] = '\0'; + /* Free the intv vector if not static. */ + if (intv != sintv) { + Jim_Free(intv); + } + + return objPtr; +} + + +/* If listPtr is a list, call JimEvalObjVector() with the given source info. + * Otherwise eval with Jim_EvalObj() + */ +static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *fileNameObj, int linenr) +{ + if (!Jim_IsList(listPtr)) { + return Jim_EvalObj(interp, listPtr); + } + else { + int retcode = JIM_OK; + + if (listPtr->internalRep.listValue.len) { + Jim_IncrRefCount(listPtr); + retcode = JimEvalObjVector(interp, + listPtr->internalRep.listValue.len, + listPtr->internalRep.listValue.ele, fileNameObj, linenr); + Jim_DecrRefCount(interp, listPtr); + } + return retcode; + } +} + +int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr) +{ + int i; + ScriptObj *script; + ScriptToken *token; + int retcode = JIM_OK; + Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL; + int linenr = 0; + + interp->errorFlag = 0; + + /* If the object is of type "list", with no string rep we can call + * a specialized version of Jim_EvalObj() */ + if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) { + return JimEvalObjList(interp, scriptObjPtr, interp->emptyObj, 1); + } + + Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */ + script = Jim_GetScript(interp, scriptObjPtr); + + /* Reset the interpreter result. This is useful to + * return the empty result in the case of empty program. */ + Jim_SetEmptyResult(interp); + +#ifdef JIM_OPTIMIZATION + /* Check for one of the following common scripts used by for, while + * + * {} + * incr a + */ + if (script->len == 0) { + Jim_DecrRefCount(interp, scriptObjPtr); + return JIM_OK; + } + if (script->len == 3 + && script->token[1].objPtr->typePtr == &commandObjType + && script->token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0 + && script->token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand + && script->token[2].objPtr->typePtr == &variableObjType) { + + Jim_Obj *objPtr = Jim_GetVariable(interp, script->token[2].objPtr, JIM_NONE); + + if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) { + JimWideValue(objPtr)++; + Jim_InvalidateStringRep(objPtr); + Jim_DecrRefCount(interp, scriptObjPtr); + Jim_SetResult(interp, objPtr); + return JIM_OK; + } + } +#endif + + /* Now we have to make sure the internal repr will not be + * freed on shimmering. + * + * Think for example to this: + * + * set x {llength $x; ... some more code ...}; eval $x + * + * In order to preserve the internal rep, we increment the + * inUse field of the script internal rep structure. */ + script->inUse++; + + token = script->token; + argv = sargv; + + /* Execute every command sequentially until the end of the script + * or an error occurs. + */ + for (i = 0; i < script->len && retcode == JIM_OK; ) { + int argc; + int j; + Jim_Cmd *cmd; + + /* First token of the line is always JIM_TT_LINE */ + argc = token[i].objPtr->internalRep.scriptLineValue.argc; + linenr = token[i].objPtr->internalRep.scriptLineValue.line; + + /* Allocate the arguments vector if required */ + if (argc > JIM_EVAL_SARGV_LEN) + argv = Jim_Alloc(sizeof(Jim_Obj *) * argc); + + /* Skip the JIM_TT_LINE token */ + i++; + + /* Populate the arguments objects. + * If an error occurs, retcode will be set and + * 'j' will be set to the number of args expanded + */ + for (j = 0; j < argc; j++) { + long wordtokens = 1; + int expand = 0; + Jim_Obj *wordObjPtr = NULL; + + if (token[i].type == JIM_TT_WORD) { + wordtokens = JimWideValue(token[i++].objPtr); + if (wordtokens < 0) { + expand = 1; + wordtokens = -wordtokens; + } + } + + if (wordtokens == 1) { + /* Fast path if the token does not + * need interpolation */ + + switch (token[i].type) { + case JIM_TT_ESC: + case JIM_TT_STR: + wordObjPtr = token[i].objPtr; + break; + case JIM_TT_VAR: + wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG); + break; + case JIM_TT_EXPRSUGAR: + wordObjPtr = JimExpandExprSugar(interp, token[i].objPtr); + break; + case JIM_TT_DICTSUGAR: + wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr); + break; + case JIM_TT_CMD: + retcode = Jim_EvalObj(interp, token[i].objPtr); + if (retcode == JIM_OK) { + wordObjPtr = Jim_GetResult(interp); + } + break; + default: + JimPanic((1, "default token type reached " "in Jim_EvalObj().")); + } + } + else { + /* For interpolation we call a helper + * function to do the work for us. */ + wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE); + } + + if (!wordObjPtr) { + if (retcode == JIM_OK) { + retcode = JIM_ERR; + } + break; + } + + Jim_IncrRefCount(wordObjPtr); + i += wordtokens; + + if (!expand) { + argv[j] = wordObjPtr; + } + else { + /* Need to expand wordObjPtr into multiple args from argv[j] ... */ + int len = Jim_ListLength(interp, wordObjPtr); + int newargc = argc + len - 1; + int k; + + if (len > 1) { + if (argv == sargv) { + if (newargc > JIM_EVAL_SARGV_LEN) { + argv = Jim_Alloc(sizeof(*argv) * newargc); + memcpy(argv, sargv, sizeof(*argv) * j); + } + } + else { + /* Need to realloc to make room for (len - 1) more entries */ + argv = Jim_Realloc(argv, sizeof(*argv) * newargc); + } + } + + /* Now copy in the expanded version */ + for (k = 0; k < len; k++) { + argv[j++] = wordObjPtr->internalRep.listValue.ele[k]; + Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]); + } + + /* The original object reference is no longer needed, + * after the expansion it is no longer present on + * the argument vector, but the single elements are + * in its place. */ + Jim_DecrRefCount(interp, wordObjPtr); + + /* And update the indexes */ + j--; + argc += len - 1; + } + } + + if (retcode == JIM_OK && argc) { + /* Lookup the command to call */ + cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG); + if (cmd != NULL) { + /* Call it -- Make sure result is an empty object. */ + JimIncrCmdRefCount(cmd); + Jim_SetEmptyResult(interp); + if (cmd->isproc) { + retcode = + JimCallProcedure(interp, cmd, script->fileNameObj, linenr, argc, argv); + } else { + interp->cmdPrivData = cmd->u.native.privData; + retcode = cmd->u.native.cmdProc(interp, argc, argv); + } + JimDecrCmdRefCount(interp, cmd); + } + else { + /* Call [unknown] */ + retcode = JimUnknown(interp, argc, argv, script->fileNameObj, linenr); + } + if (interp->signal_level && interp->sigmask) { + /* Check for a signal after each command */ + retcode = JIM_SIGNAL; + } + } + + /* Finished with the command, so decrement ref counts of each argument */ + while (j-- > 0) { + Jim_DecrRefCount(interp, argv[j]); + } + + if (argv != sargv) { + Jim_Free(argv); + argv = sargv; + } + } + + /* Possibly add to the error stack trace */ + JimAddErrorToStack(interp, retcode, script->fileNameObj, linenr); + + /* Note that we don't have to decrement inUse, because the + * following code transfers our use of the reference again to + * the script object. */ + Jim_FreeIntRep(interp, scriptObjPtr); + scriptObjPtr->typePtr = &scriptObjType; + Jim_SetIntRepPtr(scriptObjPtr, script); + Jim_DecrRefCount(interp, scriptObjPtr); + + return retcode; +} + +static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj) +{ + int retcode; + /* If argObjPtr begins with '&', do an automatic upvar */ + const char *varname = Jim_String(argNameObj); + if (*varname == '&') { + /* First check that the target variable exists */ + Jim_Obj *objPtr; + Jim_CallFrame *savedCallFrame = interp->framePtr; + + interp->framePtr = interp->framePtr->parentCallFrame; + objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG); + interp->framePtr = savedCallFrame; + if (!objPtr) { + return JIM_ERR; + } + + /* It exists, so perform the binding. */ + objPtr = Jim_NewStringObj(interp, varname + 1, -1); + Jim_IncrRefCount(objPtr); + retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parentCallFrame); + Jim_DecrRefCount(interp, objPtr); + } + else { + retcode = Jim_SetVariable(interp, argNameObj, argValObj); + } + return retcode; +} + +/** + * Sets the interp result to be an error message indicating the required proc args. + */ +static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd) +{ + /* Create a nice error message, consistent with Tcl 8.5 */ + Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0); + int i; + + for (i = 0; i < cmd->u.proc.argListLen; i++) { + Jim_AppendString(interp, argmsg, " ", 1); + + if (i == cmd->u.proc.argsPos) { + if (cmd->u.proc.arglist[i].defaultObjPtr) { + /* Renamed args */ + Jim_AppendString(interp, argmsg, "?", 1); + Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr); + Jim_AppendString(interp, argmsg, " ...?", -1); + } + else { + /* We have plain args */ + Jim_AppendString(interp, argmsg, "?argument ...?", -1); + } + } + else { + if (cmd->u.proc.arglist[i].defaultObjPtr) { + Jim_AppendString(interp, argmsg, "?", 1); + Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr); + Jim_AppendString(interp, argmsg, "?", 1); + } + else { + Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr); + } + } + } + Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg); + Jim_FreeNewObj(interp, argmsg); +} + +/* Call a procedure implemented in Tcl. + * It's possible to speed-up a lot this function, currently + * the callframes are not cached, but allocated and + * destroied every time. What is expecially costly is + * to create/destroy the local vars hash table every time. + * + * This can be fixed just implementing callframes caching + * in JimCreateCallFrame() and JimFreeCallFrame(). */ +static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, Jim_Obj *fileNameObj, int linenr, int argc, + Jim_Obj *const *argv) +{ + Jim_CallFrame *callFramePtr; + Jim_Stack *prevLocalProcs; + int i, d, retcode, optargs; + + /* Check arity */ + if (argc - 1 < cmd->u.proc.reqArity || + (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) { + JimSetProcWrongArgs(interp, argv[0], cmd); + return JIM_ERR; + } + + /* Check if there are too nested calls */ + if (interp->framePtr->level == interp->maxNestingDepth) { + Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1); + return JIM_ERR; + } + + /* Create a new callframe */ + callFramePtr = JimCreateCallFrame(interp, interp->framePtr); + callFramePtr->argv = argv; + callFramePtr->argc = argc; + callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr; + callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr; + callFramePtr->staticVars = cmd->u.proc.staticVars; + callFramePtr->fileNameObj = fileNameObj; + callFramePtr->line = linenr; + Jim_IncrRefCount(cmd->u.proc.argListObjPtr); + Jim_IncrRefCount(cmd->u.proc.bodyObjPtr); + interp->framePtr = callFramePtr; + + /* How many optional args are available */ + optargs = (argc - 1 - cmd->u.proc.reqArity); + + /* Step 'i' along the actual args, and step 'd' along the formal args */ + i = 1; + for (d = 0; d < cmd->u.proc.argListLen; d++) { + Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr; + if (d == cmd->u.proc.argsPos) { + /* assign $args */ + Jim_Obj *listObjPtr; + int argsLen = 0; + if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) { + argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity); + } + listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen); + + /* It is possible to rename args. */ + if (cmd->u.proc.arglist[d].defaultObjPtr) { + nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr; + } + retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr); + if (retcode != JIM_OK) { + goto badargset; + } + + i += argsLen; + continue; + } + + /* Optional or required? */ + if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) { + retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]); + } + else { + /* Ran out, so use the default */ + retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr); + } + if (retcode != JIM_OK) { + goto badargset; + } + } + + /* Install a new stack for local procs */ + prevLocalProcs = interp->localProcs; + interp->localProcs = NULL; + + /* Eval the body */ + retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr); + + /* Delete any local procs */ + JimDeleteLocalProcs(interp); + interp->localProcs = prevLocalProcs; + +badargset: + /* Destroy the callframe */ + interp->framePtr = interp->framePtr->parentCallFrame; + if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) { + JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE); + } + else { + JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT); + } + /* Handle the JIM_EVAL return code */ + while (retcode == JIM_EVAL) { + Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp); + + Jim_IncrRefCount(resultScriptObjPtr); + /* Should be a list! */ + retcode = JimEvalObjList(interp, resultScriptObjPtr, fileNameObj, linenr); + Jim_DecrRefCount(interp, resultScriptObjPtr); + } + /* Handle the JIM_RETURN return code */ + if (retcode == JIM_RETURN) { + if (--interp->returnLevel <= 0) { + retcode = interp->returnCode; + interp->returnCode = JIM_OK; + interp->returnLevel = 0; + } + } + else if (retcode == JIM_ERR) { + interp->addStackTrace++; + Jim_DecrRefCount(interp, interp->errorProc); + interp->errorProc = argv[0]; + Jim_IncrRefCount(interp->errorProc); + } + return retcode; +} + +int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script) +{ + int retval; + Jim_Obj *scriptObjPtr; + + scriptObjPtr = Jim_NewStringObj(interp, script, -1); + Jim_IncrRefCount(scriptObjPtr); + + if (filename) { + Jim_Obj *prevScriptObj; + + JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno); + + prevScriptObj = interp->currentScriptObj; + interp->currentScriptObj = scriptObjPtr; + + retval = Jim_EvalObj(interp, scriptObjPtr); + + interp->currentScriptObj = prevScriptObj; + } + else { + retval = Jim_EvalObj(interp, scriptObjPtr); + } + Jim_DecrRefCount(interp, scriptObjPtr); + return retval; +} + +int Jim_Eval(Jim_Interp *interp, const char *script) +{ + return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1)); +} + +/* Execute script in the scope of the global level */ +int Jim_EvalGlobal(Jim_Interp *interp, const char *script) +{ + int retval; + Jim_CallFrame *savedFramePtr = interp->framePtr; + + interp->framePtr = interp->topFramePtr; + retval = Jim_Eval(interp, script); + interp->framePtr = savedFramePtr; + + return retval; +} + +int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename) +{ + int retval; + Jim_CallFrame *savedFramePtr = interp->framePtr; + + interp->framePtr = interp->topFramePtr; + retval = Jim_EvalFile(interp, filename); + interp->framePtr = savedFramePtr; + + return retval; +} + +#include + +int Jim_EvalFile(Jim_Interp *interp, const char *filename) +{ + FILE *fp; + char *buf; + Jim_Obj *scriptObjPtr; + Jim_Obj *prevScriptObj; + struct stat sb; + int retcode; + int readlen; + struct JimParseResult result; + + if (stat(filename, &sb) != 0 || (fp = fopen(filename, "rt")) == NULL) { + Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno)); + return JIM_ERR; + } + if (sb.st_size == 0) { + fclose(fp); + return JIM_OK; + } + + buf = Jim_Alloc(sb.st_size + 1); + readlen = fread(buf, 1, sb.st_size, fp); + if (ferror(fp)) { + fclose(fp); + Jim_Free(buf); + Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno)); + return JIM_ERR; + } + fclose(fp); + buf[readlen] = 0; + + scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen); + JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1); + Jim_IncrRefCount(scriptObjPtr); + + /* Now check the script for unmatched braces, etc. */ + if (SetScriptFromAny(interp, scriptObjPtr, &result) == JIM_ERR) { + const char *msg; + char linebuf[20]; + + switch (result.missing) { + case '[': + msg = "unmatched \"[\""; + break; + case '{': + msg = "missing close-brace"; + break; + case '"': + default: + msg = "missing quote"; + break; + } + + snprintf(linebuf, sizeof(linebuf), "%d", result.line); + + Jim_SetResultFormatted(interp, "%s in \"%s\" at line %s", + msg, filename, linebuf); + Jim_DecrRefCount(interp, scriptObjPtr); + return JIM_ERR; + } + + prevScriptObj = interp->currentScriptObj; + interp->currentScriptObj = scriptObjPtr; + + retcode = Jim_EvalObj(interp, scriptObjPtr); + + /* Handle the JIM_RETURN return code */ + if (retcode == JIM_RETURN) { + if (--interp->returnLevel <= 0) { + retcode = interp->returnCode; + interp->returnCode = JIM_OK; + interp->returnLevel = 0; + } + } + if (retcode == JIM_ERR) { + /* EvalFile changes context, so add a stack frame here */ + interp->addStackTrace++; + } + + interp->currentScriptObj = prevScriptObj; + + Jim_DecrRefCount(interp, scriptObjPtr); + + return retcode; +} + +/* ----------------------------------------------------------------------------- + * Subst + * ---------------------------------------------------------------------------*/ +static int JimParseSubstStr(struct JimParserCtx *pc) +{ + pc->tstart = pc->p; + pc->tline = pc->linenr; + while (pc->len && *pc->p != '$' && *pc->p != '[') { + if (*pc->p == '\\' && pc->len > 1) { + pc->p++; + pc->len--; + } + pc->p++; + pc->len--; + } + pc->tend = pc->p - 1; + pc->tt = JIM_TT_ESC; + return JIM_OK; +} + +static int JimParseSubst(struct JimParserCtx *pc, int flags) +{ + int retval; + + if (pc->len == 0) { + pc->tstart = pc->tend = pc->p; + pc->tline = pc->linenr; + pc->tt = JIM_TT_EOL; + pc->eof = 1; + return JIM_OK; + } + switch (*pc->p) { + case '[': + retval = JimParseCmd(pc); + if (flags & JIM_SUBST_NOCMD) { + pc->tstart--; + pc->tend++; + pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC; + } + return retval; + break; + case '$': + if (JimParseVar(pc) == JIM_ERR) { + pc->tstart = pc->tend = pc->p++; + pc->len--; + pc->tline = pc->linenr; + pc->tt = JIM_TT_STR; + } + else { + if (flags & JIM_SUBST_NOVAR) { + pc->tstart--; + if (flags & JIM_SUBST_NOESC) + pc->tt = JIM_TT_STR; + else + pc->tt = JIM_TT_ESC; + if (*pc->tstart == '{') { + pc->tstart--; + if (*(pc->tend + 1)) + pc->tend++; + } + } + } + break; + default: + retval = JimParseSubstStr(pc); + if (flags & JIM_SUBST_NOESC) + pc->tt = JIM_TT_STR; + return retval; + break; + } + return JIM_OK; +} + +/* The subst object type reuses most of the data structures and functions + * of the script object. Script's data structures are a bit more complex + * for what is needed for [subst]itution tasks, but the reuse helps to + * deal with a single data structure at the cost of some more memory + * usage for substitutions. */ + +/* This method takes the string representation of an object + * as a Tcl string where to perform [subst]itution, and generates + * the pre-parsed internal representation. */ +static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags) +{ + int scriptTextLen; + const char *scriptText = Jim_GetString(objPtr, &scriptTextLen); + struct JimParserCtx parser; + struct ScriptObj *script = Jim_Alloc(sizeof(*script)); + ParseTokenList tokenlist; + + /* Initially parse the subst into tokens (in tokenlist) */ + ScriptTokenListInit(&tokenlist); + + JimParserInit(&parser, scriptText, scriptTextLen, 1); + while (1) { + JimParseSubst(&parser, flags); + if (parser.eof) { + /* Note that subst doesn't need the EOL token */ + break; + } + ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, + parser.tline); + } + + /* Create the "real" subst/script tokens from the initial token list */ + script->inUse = 1; + script->substFlags = flags; + script->fileNameObj = interp->emptyObj; + Jim_IncrRefCount(script->fileNameObj); + SubstObjAddTokens(interp, script, &tokenlist); + + /* No longer need the token list */ + ScriptTokenListFree(&tokenlist); + +#ifdef DEBUG_SHOW_SUBST + { + int i; + + printf("==== Subst ====\n"); + for (i = 0; i < script->len; i++) { + printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type), + Jim_String(script->token[i].objPtr)); + } + } +#endif + + /* Free the old internal rep and set the new one. */ + Jim_FreeIntRep(interp, objPtr); + Jim_SetIntRepPtr(objPtr, script); + objPtr->typePtr = &scriptObjType; + return JIM_OK; +} + +static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags) +{ + if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags) + SetSubstFromAny(interp, objPtr, flags); + return (ScriptObj *) Jim_GetIntRepPtr(objPtr); +} + +/* Performs commands,variables,blackslashes substitution, + * storing the result object (with refcount 0) into + * resObjPtrPtr. */ +int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags) +{ + ScriptObj *script = Jim_GetSubst(interp, substObjPtr, flags); + + Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */ + /* In order to preserve the internal rep, we increment the + * inUse field of the script internal rep structure. */ + script->inUse++; + + *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags); + + script->inUse--; + Jim_DecrRefCount(interp, substObjPtr); + if (*resObjPtrPtr == NULL) { + return JIM_ERR; + } + return JIM_OK; +} + +/* ----------------------------------------------------------------------------- + * Core commands utility functions + * ---------------------------------------------------------------------------*/ +void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg) +{ + int i; + Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp); + + Jim_AppendString(interp, objPtr, "wrong # args: should be \"", -1); + for (i = 0; i < argc; i++) { + Jim_AppendObj(interp, objPtr, argv[i]); + if (!(i + 1 == argc && msg[0] == '\0')) + Jim_AppendString(interp, objPtr, " ", 1); + } + Jim_AppendString(interp, objPtr, msg, -1); + Jim_AppendString(interp, objPtr, "\"", 1); + Jim_SetResult(interp, objPtr); +} + +#define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL) + +/* type is: 0=commands, 1=procs, 2=channels */ +static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type) +{ + Jim_HashTableIterator *htiter; + Jim_HashEntry *he; + Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); + + /* Check for the non-pattern case. We can do this much more efficiently. */ + if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) { + Jim_Cmd *cmdPtr = Jim_GetCommand(interp, patternObjPtr, JIM_NONE); + if (cmdPtr) { + if (type == 1 && !cmdPtr->isproc) { + /* not a proc */ + } + else if (type == 2 && !Jim_AioFilehandle(interp, patternObjPtr)) { + /* not a channel */ + } + else { + Jim_ListAppendElement(interp, listObjPtr, patternObjPtr); + } + } + return listObjPtr; + } + + htiter = Jim_GetHashTableIterator(&interp->commands); + while ((he = Jim_NextHashEntry(htiter)) != NULL) { + Jim_Cmd *cmdPtr = he->u.val; + Jim_Obj *cmdNameObj; + + if (type == 1 && !cmdPtr->isproc) { + /* not a proc */ + continue; + } + if (patternObjPtr && !JimStringMatch(interp, patternObjPtr, he->key, 0)) + continue; + + cmdNameObj = Jim_NewStringObj(interp, he->key, -1); + + /* Is it a channel? */ + if (type == 2 && !Jim_AioFilehandle(interp, cmdNameObj)) { + Jim_FreeNewObj(interp, cmdNameObj); + continue; + } + + Jim_ListAppendElement(interp, listObjPtr, cmdNameObj); + } + Jim_FreeHashTableIterator(htiter); + return listObjPtr; +} + +/* Keep this in order */ +#define JIM_VARLIST_GLOBALS 0 +#define JIM_VARLIST_LOCALS 1 +#define JIM_VARLIST_VARS 2 + +static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode) +{ + Jim_HashTableIterator *htiter; + Jim_HashEntry *he; + Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); + + if (mode == JIM_VARLIST_GLOBALS) { + htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars); + } + else { + /* For [info locals], if we are at top level an emtpy list + * is returned. I don't agree, but we aim at compatibility (SS) */ + if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) + return listObjPtr; + htiter = Jim_GetHashTableIterator(&interp->framePtr->vars); + } + while ((he = Jim_NextHashEntry(htiter)) != NULL) { + Jim_Var *varPtr = (Jim_Var *)he->u.val; + + if (mode == JIM_VARLIST_LOCALS) { + if (varPtr->linkFramePtr != NULL) + continue; + } + if (patternObjPtr && !JimStringMatch(interp, patternObjPtr, he->key, 0)) + continue; + Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, he->key, -1)); + } + Jim_FreeHashTableIterator(htiter); + return listObjPtr; +} + +static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr, + Jim_Obj **objPtrPtr, int info_level_cmd) +{ + Jim_CallFrame *targetCallFrame; + + targetCallFrame = JimGetCallFrameByInteger(interp, levelObjPtr); + if (targetCallFrame == NULL) { + return JIM_ERR; + } + /* No proc call at toplevel callframe */ + if (targetCallFrame == interp->topFramePtr) { + Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr); + return JIM_ERR; + } + if (info_level_cmd) { + *objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc); + } + else { + Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); + + Jim_ListAppendElement(interp, listObj, targetCallFrame->argv[0]); + Jim_ListAppendElement(interp, listObj, targetCallFrame->fileNameObj); + Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, targetCallFrame->line)); + *objPtrPtr = listObj; + } + return JIM_OK; +} + +/* ----------------------------------------------------------------------------- + * Core commands + * ---------------------------------------------------------------------------*/ + +/* fake [puts] -- not the real puts, just for debugging. */ +static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc != 2 && argc != 3) { + Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string"); + return JIM_ERR; + } + if (argc == 3) { + if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) { + Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1); + return JIM_ERR; + } + else { + fputs(Jim_String(argv[2]), stdout); + } + } + else { + puts(Jim_String(argv[1])); + } + return JIM_OK; +} + +/* Helper for [+] and [*] */ +static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op) +{ + jim_wide wideValue, res; + double doubleValue, doubleRes; + int i; + + res = (op == JIM_EXPROP_ADD) ? 0 : 1; + + for (i = 1; i < argc; i++) { + if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) + goto trydouble; + if (op == JIM_EXPROP_ADD) + res += wideValue; + else + res *= wideValue; + } + Jim_SetResultInt(interp, res); + return JIM_OK; + trydouble: + doubleRes = (double)res; + for (; i < argc; i++) { + if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK) + return JIM_ERR; + if (op == JIM_EXPROP_ADD) + doubleRes += doubleValue; + else + doubleRes *= doubleValue; + } + Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); + return JIM_OK; +} + +/* Helper for [-] and [/] */ +static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op) +{ + jim_wide wideValue, res = 0; + double doubleValue, doubleRes = 0; + int i = 2; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?"); + return JIM_ERR; + } + else if (argc == 2) { + /* The arity = 2 case is different. For [- x] returns -x, + * while [/ x] returns 1/x. */ + if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) { + if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) { + return JIM_ERR; + } + else { + if (op == JIM_EXPROP_SUB) + doubleRes = -doubleValue; + else + doubleRes = 1.0 / doubleValue; + Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); + return JIM_OK; + } + } + if (op == JIM_EXPROP_SUB) { + res = -wideValue; + Jim_SetResultInt(interp, res); + } + else { + doubleRes = 1.0 / wideValue; + Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); + } + return JIM_OK; + } + else { + if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) { + if (Jim_GetDouble(interp, argv[1], &doubleRes) + != JIM_OK) { + return JIM_ERR; + } + else { + goto trydouble; + } + } + } + for (i = 2; i < argc; i++) { + if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) { + doubleRes = (double)res; + goto trydouble; + } + if (op == JIM_EXPROP_SUB) + res -= wideValue; + else + res /= wideValue; + } + Jim_SetResultInt(interp, res); + return JIM_OK; + trydouble: + for (; i < argc; i++) { + if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK) + return JIM_ERR; + if (op == JIM_EXPROP_SUB) + doubleRes -= doubleValue; + else + doubleRes /= doubleValue; + } + Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); + return JIM_OK; +} + + +/* [+] */ +static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD); +} + +/* [*] */ +static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL); +} + +/* [-] */ +static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB); +} + +/* [/] */ +static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV); +} + +/* [set] */ +static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc != 2 && argc != 3) { + Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?"); + return JIM_ERR; + } + if (argc == 2) { + Jim_Obj *objPtr; + + objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG); + if (!objPtr) + return JIM_ERR; + Jim_SetResult(interp, objPtr); + return JIM_OK; + } + /* argc == 3 case. */ + if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK) + return JIM_ERR; + Jim_SetResult(interp, argv[2]); + return JIM_OK; +} + +/* [unset] + * + * unset ?-nocomplain? ?--? ?varName ...? + */ +static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int i = 1; + int complain = 1; + + while (i < argc) { + if (Jim_CompareStringImmediate(interp, argv[i], "--")) { + i++; + break; + } + if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) { + complain = 0; + i++; + continue; + } + break; + } + + while (i < argc) { + if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK + && complain) { + return JIM_ERR; + } + i++; + } + return JIM_OK; +} + +/* [while] */ +static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc != 3) { + Jim_WrongNumArgs(interp, 1, argv, "condition body"); + return JIM_ERR; + } + + /* The general purpose implementation of while starts here */ + while (1) { + int boolean, retval; + + if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK) + return retval; + if (!boolean) + break; + + if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) { + switch (retval) { + case JIM_BREAK: + goto out; + break; + case JIM_CONTINUE: + continue; + break; + default: + return retval; + } + } + } + out: + Jim_SetEmptyResult(interp); + return JIM_OK; +} + +/* [for] */ +static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int retval; + int boolean = 1; + Jim_Obj *varNamePtr = NULL; + Jim_Obj *stopVarNamePtr = NULL; + + if (argc != 5) { + Jim_WrongNumArgs(interp, 1, argv, "start test next body"); + return JIM_ERR; + } + + /* Do the initialisation */ + if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) { + return retval; + } + + /* And do the first test now. Better for optimisation + * if we can do next/test at the bottom of the loop + */ + retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean); + + /* Ready to do the body as follows: + * while (1) { + * body // check retcode + * next // check retcode + * test // check retcode/test bool + * } + */ + +#ifdef JIM_OPTIMIZATION + /* Check if the for is on the form: + * for ... {$i < CONST} {incr i} + * for ... {$i < $j} {incr i} + */ + if (retval == JIM_OK && boolean) { + ScriptObj *incrScript; + ExprByteCode *expr; + jim_wide stop, currentVal; + unsigned jim_wide procEpoch; + Jim_Obj *objPtr; + int cmpOffset; + + /* Do it only if there aren't shared arguments */ + expr = JimGetExpression(interp, argv[2]); + incrScript = Jim_GetScript(interp, argv[3]); + + /* Ensure proper lengths to start */ + if (incrScript->len != 3 || !expr || expr->len != 3) { + goto evalstart; + } + /* Ensure proper token types. */ + if (incrScript->token[1].type != JIM_TT_ESC || + expr->token[0].type != JIM_TT_VAR || + (expr->token[1].type != JIM_TT_EXPR_INT && expr->token[1].type != JIM_TT_VAR)) { + goto evalstart; + } + + if (expr->token[2].type == JIM_EXPROP_LT) { + cmpOffset = 0; + } + else if (expr->token[2].type == JIM_EXPROP_LTE) { + cmpOffset = 1; + } + else { + goto evalstart; + } + + /* Update command must be incr */ + if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) { + goto evalstart; + } + + /* incr, expression must be about the same variable */ + if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->token[0].objPtr)) { + goto evalstart; + } + + /* Get the stop condition (must be a variable or integer) */ + if (expr->token[1].type == JIM_TT_EXPR_INT) { + if (Jim_GetWide(interp, expr->token[1].objPtr, &stop) == JIM_ERR) { + goto evalstart; + } + } + else { + stopVarNamePtr = expr->token[1].objPtr; + Jim_IncrRefCount(stopVarNamePtr); + /* Keep the compiler happy */ + stop = 0; + } + + /* Initialization */ + procEpoch = interp->procEpoch; + varNamePtr = expr->token[0].objPtr; + Jim_IncrRefCount(varNamePtr); + + objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE); + if (objPtr == NULL || Jim_GetWide(interp, objPtr, ¤tVal) != JIM_OK) { + goto testcond; + } + + /* --- OPTIMIZED FOR --- */ + while (retval == JIM_OK) { + /* === Check condition === */ + /* Note that currentVal is already set here */ + + /* Immediate or Variable? get the 'stop' value if the latter. */ + if (stopVarNamePtr) { + objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE); + if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) { + goto testcond; + } + } + + if (currentVal >= stop + cmpOffset) { + break; + } + + /* Eval body */ + retval = Jim_EvalObj(interp, argv[4]); + if (retval == JIM_OK || retval == JIM_CONTINUE) { + retval = JIM_OK; + /* If there was a change in procedures/command continue + * with the usual [for] command implementation */ + if (procEpoch != interp->procEpoch) { + goto evalnext; + } + + objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG); + + /* Increment */ + if (objPtr == NULL) { + retval = JIM_ERR; + goto out; + } + if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) { + currentVal = ++JimWideValue(objPtr); + Jim_InvalidateStringRep(objPtr); + } + else { + if (Jim_GetWide(interp, objPtr, ¤tVal) != JIM_OK || + Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp, + ++currentVal)) != JIM_OK) { + goto evalnext; + } + } + } + } + goto out; + } + evalstart: +#endif + + while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) { + /* Body */ + retval = Jim_EvalObj(interp, argv[4]); + + if (retval == JIM_OK || retval == JIM_CONTINUE) { + /* increment */ + evalnext: + retval = Jim_EvalObj(interp, argv[3]); + if (retval == JIM_OK || retval == JIM_CONTINUE) { + /* test */ + testcond: + retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean); + } + } + } + out: + if (stopVarNamePtr) { + Jim_DecrRefCount(interp, stopVarNamePtr); + } + if (varNamePtr) { + Jim_DecrRefCount(interp, varNamePtr); + } + + if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) { + Jim_SetEmptyResult(interp); + return JIM_OK; + } + + return retval; +} + +/* [loop] */ +static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int retval; + jim_wide i; + jim_wide limit; + jim_wide incr = 1; + Jim_Obj *bodyObjPtr; + + if (argc != 5 && argc != 6) { + Jim_WrongNumArgs(interp, 1, argv, "var first limit ?incr? body"); + return JIM_ERR; + } + + if (Jim_GetWide(interp, argv[2], &i) != JIM_OK || + Jim_GetWide(interp, argv[3], &limit) != JIM_OK || + (argc == 6 && Jim_GetWide(interp, argv[4], &incr) != JIM_OK)) { + return JIM_ERR; + } + bodyObjPtr = (argc == 5) ? argv[4] : argv[5]; + + retval = Jim_SetVariable(interp, argv[1], argv[2]); + + while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) { + retval = Jim_EvalObj(interp, bodyObjPtr); + if (retval == JIM_OK || retval == JIM_CONTINUE) { + Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG); + + retval = JIM_OK; + + /* Increment */ + i += incr; + + if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) { + if (argv[1]->typePtr != &variableObjType) { + if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) { + return JIM_ERR; + } + } + JimWideValue(objPtr) = i; + Jim_InvalidateStringRep(objPtr); + + /* The following step is required in order to invalidate the + * string repr of "FOO" if the var name is of the form of "FOO(IDX)" */ + if (argv[1]->typePtr != &variableObjType) { + if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) { + retval = JIM_ERR; + break; + } + } + } + else { + objPtr = Jim_NewIntObj(interp, i); + retval = Jim_SetVariable(interp, argv[1], objPtr); + if (retval != JIM_OK) { + Jim_FreeNewObj(interp, objPtr); + } + } + } + } + + if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) { + Jim_SetEmptyResult(interp); + return JIM_OK; + } + return retval; +} + +/* foreach + lmap implementation. */ +static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap) +{ + int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd; + int nbrOfLoops = 0; + Jim_Obj *emptyStr, *script, *mapRes = NULL; + + if (argc < 4 || argc % 2 != 0) { + Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script"); + return JIM_ERR; + } + if (doMap) { + mapRes = Jim_NewListObj(interp, NULL, 0); + Jim_IncrRefCount(mapRes); + } + emptyStr = Jim_NewEmptyStringObj(interp); + Jim_IncrRefCount(emptyStr); + script = argv[argc - 1]; /* Last argument is a script */ + nbrOfLists = (argc - 1 - 1) / 2; /* argc - 'foreach' - script */ + listsIdx = (int *)Jim_Alloc(nbrOfLists * sizeof(int)); + listsEnd = (int *)Jim_Alloc(nbrOfLists * 2 * sizeof(int)); + /* Initialize iterators and remember max nbr elements each list */ + memset(listsIdx, 0, nbrOfLists * sizeof(int)); + /* Remember lengths of all lists and calculate how much rounds to loop */ + for (i = 0; i < nbrOfLists * 2; i += 2) { + div_t cnt; + int count; + + listsEnd[i] = Jim_ListLength(interp, argv[i + 1]); + listsEnd[i + 1] = Jim_ListLength(interp, argv[i + 2]); + if (listsEnd[i] == 0) { + Jim_SetResultString(interp, "foreach varlist is empty", -1); + goto err; + } + cnt = div(listsEnd[i + 1], listsEnd[i]); + count = cnt.quot + (cnt.rem ? 1 : 0); + if (count > nbrOfLoops) + nbrOfLoops = count; + } + for (; nbrOfLoops-- > 0;) { + for (i = 0; i < nbrOfLists; ++i) { + int varIdx = 0, var = i * 2; + + while (varIdx < listsEnd[var]) { + Jim_Obj *varName, *ele; + int lst = i * 2 + 1; + + /* List index operations below can't fail */ + Jim_ListIndex(interp, argv[var + 1], varIdx, &varName, JIM_NONE); + if (listsIdx[i] < listsEnd[lst]) { + Jim_ListIndex(interp, argv[lst + 1], listsIdx[i], &ele, JIM_NONE); + /* Avoid shimmering */ + Jim_IncrRefCount(ele); + result = Jim_SetVariable(interp, varName, ele); + Jim_DecrRefCount(interp, ele); + if (result == JIM_OK) { + ++listsIdx[i]; /* Remember next iterator of current list */ + ++varIdx; /* Next variable */ + continue; + } + } + else if (Jim_SetVariable(interp, varName, emptyStr) == JIM_OK) { + ++varIdx; /* Next variable */ + continue; + } + goto err; + } + } + switch (result = Jim_EvalObj(interp, script)) { + case JIM_OK: + if (doMap) + Jim_ListAppendElement(interp, mapRes, interp->result); + break; + case JIM_CONTINUE: + break; + case JIM_BREAK: + goto out; + break; + default: + goto err; + } + } + out: + result = JIM_OK; + if (doMap) + Jim_SetResult(interp, mapRes); + else + Jim_SetEmptyResult(interp); + err: + if (doMap) + Jim_DecrRefCount(interp, mapRes); + Jim_DecrRefCount(interp, emptyStr); + Jim_Free(listsIdx); + Jim_Free(listsEnd); + return result; +} + +/* [foreach] */ +static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return JimForeachMapHelper(interp, argc, argv, 0); +} + +/* [lmap] */ +static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + return JimForeachMapHelper(interp, argc, argv, 1); +} + +/* [if] */ +static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int boolean, retval, current = 1, falsebody = 0; + + if (argc >= 3) { + while (1) { + /* Far not enough arguments given! */ + if (current >= argc) + goto err; + if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean)) + != JIM_OK) + return retval; + /* There lacks something, isn't it? */ + if (current >= argc) + goto err; + if (Jim_CompareStringImmediate(interp, argv[current], "then")) + current++; + /* Tsk tsk, no then-clause? */ + if (current >= argc) + goto err; + if (boolean) + return Jim_EvalObj(interp, argv[current]); + /* Ok: no else-clause follows */ + if (++current >= argc) { + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + return JIM_OK; + } + falsebody = current++; + if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) { + /* IIICKS - else-clause isn't last cmd? */ + if (current != argc - 1) + goto err; + return Jim_EvalObj(interp, argv[current]); + } + else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif")) + /* Ok: elseif follows meaning all the stuff + * again (how boring...) */ + continue; + /* OOPS - else-clause is not last cmd? */ + else if (falsebody != argc - 1) + goto err; + return Jim_EvalObj(interp, argv[falsebody]); + } + return JIM_OK; + } + err: + Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody"); + return JIM_ERR; +} + + +/* Returns 1 if match, 0 if no match or - on error (e.g. -JIM_ERR, -JIM_BREAK)*/ +int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj, + Jim_Obj *stringObj, int nocase) +{ + Jim_Obj *parms[4]; + int argc = 0; + long eq; + int rc; + + parms[argc++] = commandObj; + if (nocase) { + parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1); + } + parms[argc++] = patternObj; + parms[argc++] = stringObj; + + rc = Jim_EvalObjVector(interp, argc, parms); + + if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) { + eq = -rc; + } + + return eq; +} + +enum +{ SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD }; + +/* [switch] */ +static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int matchOpt = SWITCH_EXACT, opt = 1, patCount, i; + Jim_Obj *command = 0, *const *caseList = 0, *strObj; + Jim_Obj *script = 0; + + if (argc < 3) { + wrongnumargs: + Jim_WrongNumArgs(interp, 1, argv, "?options? string " + "pattern body ... ?default body? or " "{pattern body ?pattern body ...?}"); + return JIM_ERR; + } + for (opt = 1; opt < argc; ++opt) { + const char *option = Jim_String(argv[opt]); + + if (*option != '-') + break; + else if (strncmp(option, "--", 2) == 0) { + ++opt; + break; + } + else if (strncmp(option, "-exact", 2) == 0) + matchOpt = SWITCH_EXACT; + else if (strncmp(option, "-glob", 2) == 0) + matchOpt = SWITCH_GLOB; + else if (strncmp(option, "-regexp", 2) == 0) + matchOpt = SWITCH_RE; + else if (strncmp(option, "-command", 2) == 0) { + matchOpt = SWITCH_CMD; + if ((argc - opt) < 2) + goto wrongnumargs; + command = argv[++opt]; + } + else { + Jim_SetResultFormatted(interp, + "bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --", + argv[opt]); + return JIM_ERR; + } + if ((argc - opt) < 2) + goto wrongnumargs; + } + strObj = argv[opt++]; + patCount = argc - opt; + if (patCount == 1) { + Jim_Obj **vector; + + JimListGetElements(interp, argv[opt], &patCount, &vector); + caseList = vector; + } + else + caseList = &argv[opt]; + if (patCount == 0 || patCount % 2 != 0) + goto wrongnumargs; + for (i = 0; script == 0 && i < patCount; i += 2) { + Jim_Obj *patObj = caseList[i]; + + if (!Jim_CompareStringImmediate(interp, patObj, "default") + || i < (patCount - 2)) { + switch (matchOpt) { + case SWITCH_EXACT: + if (Jim_StringEqObj(strObj, patObj)) + script = caseList[i + 1]; + break; + case SWITCH_GLOB: + if (Jim_StringMatchObj(interp, patObj, strObj, 0)) + script = caseList[i + 1]; + break; + case SWITCH_RE: + command = Jim_NewStringObj(interp, "regexp", -1); + /* Fall thru intentionally */ + case SWITCH_CMD:{ + int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, 0); + + /* After the execution of a command we need to + * make sure to reconvert the object into a list + * again. Only for the single-list style [switch]. */ + if (argc - opt == 1) { + Jim_Obj **vector; + + JimListGetElements(interp, argv[opt], &patCount, &vector); + caseList = vector; + } + /* command is here already decref'd */ + if (rc < 0) { + return -rc; + } + if (rc) + script = caseList[i + 1]; + break; + } + } + } + else { + script = caseList[i + 1]; + } + } + for (; i < patCount && Jim_CompareStringImmediate(interp, script, "-"); i += 2) + script = caseList[i + 1]; + if (script && Jim_CompareStringImmediate(interp, script, "-")) { + Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]); + return JIM_ERR; + } + Jim_SetEmptyResult(interp); + if (script) { + return Jim_EvalObj(interp, script); + } + return JIM_OK; +} + +/* [list] */ +static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *listObjPtr; + + listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1); + Jim_SetResult(interp, listObjPtr); + return JIM_OK; +} + +/* [lindex] */ +static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *objPtr, *listObjPtr; + int i; + int idx; + + if (argc < 3) { + Jim_WrongNumArgs(interp, 1, argv, "list index ?...?"); + return JIM_ERR; + } + objPtr = argv[1]; + Jim_IncrRefCount(objPtr); + for (i = 2; i < argc; i++) { + listObjPtr = objPtr; + if (Jim_GetIndex(interp, argv[i], &idx) != JIM_OK) { + Jim_DecrRefCount(interp, listObjPtr); + return JIM_ERR; + } + if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_NONE) != JIM_OK) { + /* Returns an empty object if the index + * is out of range. */ + Jim_DecrRefCount(interp, listObjPtr); + Jim_SetEmptyResult(interp); + return JIM_OK; + } + Jim_IncrRefCount(objPtr); + Jim_DecrRefCount(interp, listObjPtr); + } + Jim_SetResult(interp, objPtr); + Jim_DecrRefCount(interp, objPtr); + return JIM_OK; +} + +/* [llength] */ +static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc != 2) { + Jim_WrongNumArgs(interp, 1, argv, "list"); + return JIM_ERR; + } + Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1])); + return JIM_OK; +} + +/* [lsearch] */ +static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + static const char * const options[] = { + "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command", + NULL + }; + enum + { OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE, + OPT_COMMAND }; + int i; + int opt_bool = 0; + int opt_not = 0; + int opt_nocase = 0; + int opt_all = 0; + int opt_inline = 0; + int opt_match = OPT_EXACT; + int listlen; + int rc = JIM_OK; + Jim_Obj *listObjPtr = NULL; + Jim_Obj *commandObj = NULL; + + if (argc < 3) { + wrongargs: + Jim_WrongNumArgs(interp, 1, argv, + "?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? list value"); + return JIM_ERR; + } + + for (i = 1; i < argc - 2; i++) { + int option; + + if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; + } + switch (option) { + case OPT_BOOL: + opt_bool = 1; + opt_inline = 0; + break; + case OPT_NOT: + opt_not = 1; + break; + case OPT_NOCASE: + opt_nocase = 1; + break; + case OPT_INLINE: + opt_inline = 1; + opt_bool = 0; + break; + case OPT_ALL: + opt_all = 1; + break; + case OPT_COMMAND: + if (i >= argc - 2) { + goto wrongargs; + } + commandObj = argv[++i]; + /* fallthru */ + case OPT_EXACT: + case OPT_GLOB: + case OPT_REGEXP: + opt_match = option; + break; + } + } + + argv += i; + + if (opt_all) { + listObjPtr = Jim_NewListObj(interp, NULL, 0); + } + if (opt_match == OPT_REGEXP) { + commandObj = Jim_NewStringObj(interp, "regexp", -1); + } + if (commandObj) { + Jim_IncrRefCount(commandObj); + } + + listlen = Jim_ListLength(interp, argv[0]); + for (i = 0; i < listlen; i++) { + Jim_Obj *objPtr; + int eq = 0; + + Jim_ListIndex(interp, argv[0], i, &objPtr, JIM_NONE); + switch (opt_match) { + case OPT_EXACT: + eq = Jim_StringCompareObj(interp, objPtr, argv[1], opt_nocase) == 0; + break; + + case OPT_GLOB: + eq = Jim_StringMatchObj(interp, argv[1], objPtr, opt_nocase); + break; + + case OPT_REGEXP: + case OPT_COMMAND: + eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, opt_nocase); + if (eq < 0) { + if (listObjPtr) { + Jim_FreeNewObj(interp, listObjPtr); + } + rc = JIM_ERR; + goto done; + } + break; + } + + /* If we have a non-match with opt_bool, opt_not, !opt_all, can't exit early */ + if (!eq && opt_bool && opt_not && !opt_all) { + continue; + } + + if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) { + /* Got a match (or non-match for opt_not), or (opt_bool && opt_all) */ + Jim_Obj *resultObj; + + if (opt_bool) { + resultObj = Jim_NewIntObj(interp, eq ^ opt_not); + } + else if (!opt_inline) { + resultObj = Jim_NewIntObj(interp, i); + } + else { + resultObj = objPtr; + } + + if (opt_all) { + Jim_ListAppendElement(interp, listObjPtr, resultObj); + } + else { + Jim_SetResult(interp, resultObj); + goto done; + } + } + } + + if (opt_all) { + Jim_SetResult(interp, listObjPtr); + } + else { + /* No match */ + if (opt_bool) { + Jim_SetResultBool(interp, opt_not); + } + else if (!opt_inline) { + Jim_SetResultInt(interp, -1); + } + } + + done: + if (commandObj) { + Jim_DecrRefCount(interp, commandObj); + } + return rc; +} + +/* [lappend] */ +static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *listObjPtr; + int shared, i; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?"); + return JIM_ERR; + } + listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED); + if (!listObjPtr) { + /* Create the list if it does not exists */ + listObjPtr = Jim_NewListObj(interp, NULL, 0); + if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) { + Jim_FreeNewObj(interp, listObjPtr); + return JIM_ERR; + } + } + shared = Jim_IsShared(listObjPtr); + if (shared) + listObjPtr = Jim_DuplicateObj(interp, listObjPtr); + for (i = 2; i < argc; i++) + Jim_ListAppendElement(interp, listObjPtr, argv[i]); + if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) { + if (shared) + Jim_FreeNewObj(interp, listObjPtr); + return JIM_ERR; + } + Jim_SetResult(interp, listObjPtr); + return JIM_OK; +} + +/* [linsert] */ +static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int idx, len; + Jim_Obj *listPtr; + + if (argc < 4) { + Jim_WrongNumArgs(interp, 1, argv, "list index element " "?element ...?"); + return JIM_ERR; + } + listPtr = argv[1]; + if (Jim_IsShared(listPtr)) + listPtr = Jim_DuplicateObj(interp, listPtr); + if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK) + goto err; + len = Jim_ListLength(interp, listPtr); + if (idx >= len) + idx = len; + else if (idx < 0) + idx = len + idx + 1; + Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]); + Jim_SetResult(interp, listPtr); + return JIM_OK; + err: + if (listPtr != argv[1]) { + Jim_FreeNewObj(interp, listPtr); + } + return JIM_ERR; +} + +/* [lreplace] */ +static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int first, last, len, rangeLen; + Jim_Obj *listObj; + Jim_Obj *newListObj; + int i; + int shared; + + if (argc < 4) { + Jim_WrongNumArgs(interp, 1, argv, "list first last ?element element ...?"); + return JIM_ERR; + } + if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK || + Jim_GetIndex(interp, argv[3], &last) != JIM_OK) { + return JIM_ERR; + } + + listObj = argv[1]; + len = Jim_ListLength(interp, listObj); + + first = JimRelToAbsIndex(len, first); + last = JimRelToAbsIndex(len, last); + JimRelToAbsRange(len, first, last, &first, &last, &rangeLen); + + /* Now construct a new list which consists of: + * + */ + + /* Check to see if trying to replace past the end of the list */ + if (first < len) { + /* OK. Not past the end */ + } + else if (len == 0) { + /* Special for empty list, adjust first to 0 */ + first = 0; + } + else { + Jim_SetResultString(interp, "list doesn't contain element ", -1); + Jim_AppendObj(interp, Jim_GetResult(interp), argv[2]); + return JIM_ERR; + } + + newListObj = Jim_NewListObj(interp, NULL, 0); + + shared = Jim_IsShared(listObj); + if (shared) { + listObj = Jim_DuplicateObj(interp, listObj); + } + + /* Add the first set of elements */ + for (i = 0; i < first; i++) { + Jim_ListAppendElement(interp, newListObj, listObj->internalRep.listValue.ele[i]); + } + + /* Add supplied elements */ + for (i = 4; i < argc; i++) { + Jim_ListAppendElement(interp, newListObj, argv[i]); + } + + /* Add the remaining elements */ + for (i = first + rangeLen; i < len; i++) { + Jim_ListAppendElement(interp, newListObj, listObj->internalRep.listValue.ele[i]); + } + Jim_SetResult(interp, newListObj); + if (shared) { + Jim_FreeNewObj(interp, listObj); + } + return JIM_OK; +} + +/* [lset] */ +static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc < 3) { + Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal"); + return JIM_ERR; + } + else if (argc == 3) { + if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK) + return JIM_ERR; + Jim_SetResult(interp, argv[2]); + return JIM_OK; + } + if (Jim_SetListIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]) + == JIM_ERR) + return JIM_ERR; + return JIM_OK; +} + +/* [lsort] */ +static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[]) +{ + static const char * const options[] = { + "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-index", NULL + }; + enum + { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_INDEX }; + Jim_Obj *resObj; + int i; + int retCode; + + struct lsort_info info; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "?options? list"); + return JIM_ERR; + } + + info.type = JIM_LSORT_ASCII; + info.order = 1; + info.indexed = 0; + info.command = NULL; + info.interp = interp; + + for (i = 1; i < (argc - 1); i++) { + int option; + + if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) + != JIM_OK) + return JIM_ERR; + switch (option) { + case OPT_ASCII: + info.type = JIM_LSORT_ASCII; + break; + case OPT_NOCASE: + info.type = JIM_LSORT_NOCASE; + break; + case OPT_INTEGER: + info.type = JIM_LSORT_INTEGER; + break; + case OPT_INCREASING: + info.order = 1; + break; + case OPT_DECREASING: + info.order = -1; + break; + case OPT_COMMAND: + if (i >= (argc - 2)) { + Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1); + return JIM_ERR; + } + info.type = JIM_LSORT_COMMAND; + info.command = argv[i + 1]; + i++; + break; + case OPT_INDEX: + if (i >= (argc - 2)) { + Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1); + return JIM_ERR; + } + if (Jim_GetIndex(interp, argv[i + 1], &info.index) != JIM_OK) { + return JIM_ERR; + } + info.indexed = 1; + i++; + break; + } + } + resObj = Jim_DuplicateObj(interp, argv[argc - 1]); + retCode = ListSortElements(interp, resObj, &info); + if (retCode == JIM_OK) { + Jim_SetResult(interp, resObj); + } + else { + Jim_FreeNewObj(interp, resObj); + } + return retCode; +} + +/* [append] */ +static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *stringObjPtr; + int i; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?"); + return JIM_ERR; + } + if (argc == 2) { + stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG); + if (!stringObjPtr) + return JIM_ERR; + } + else { + int freeobj = 0; + stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED); + if (!stringObjPtr) { + /* Create the string if it doesn't exist */ + stringObjPtr = Jim_NewEmptyStringObj(interp); + freeobj = 1; + } + else if (Jim_IsShared(stringObjPtr)) { + freeobj = 1; + stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr); + } + for (i = 2; i < argc; i++) { + Jim_AppendObj(interp, stringObjPtr, argv[i]); + } + if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) { + if (freeobj) { + Jim_FreeNewObj(interp, stringObjPtr); + } + return JIM_ERR; + } + } + Jim_SetResult(interp, stringObjPtr); + return JIM_OK; +} + +/* [debug] */ +static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ +#if defined(JIM_DEBUG_COMMAND) && !defined(JIM_BOOTSTRAP) + static const char * const options[] = { + "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen", + "exprbc", "show", + NULL + }; + enum + { + OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN, + OPT_EXPRLEN, OPT_EXPRBC, OPT_SHOW, + }; + int option; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "subcommand ?...?"); + return JIM_ERR; + } + if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK) + return JIM_ERR; + if (option == OPT_REFCOUNT) { + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "object"); + return JIM_ERR; + } + Jim_SetResultInt(interp, argv[2]->refCount); + return JIM_OK; + } + else if (option == OPT_OBJCOUNT) { + int freeobj = 0, liveobj = 0; + char buf[256]; + Jim_Obj *objPtr; + + if (argc != 2) { + Jim_WrongNumArgs(interp, 2, argv, ""); + return JIM_ERR; + } + /* Count the number of free objects. */ + objPtr = interp->freeList; + while (objPtr) { + freeobj++; + objPtr = objPtr->nextObjPtr; + } + /* Count the number of live objects. */ + objPtr = interp->liveList; + while (objPtr) { + liveobj++; + objPtr = objPtr->nextObjPtr; + } + /* Set the result string and return. */ + sprintf(buf, "free %d used %d", freeobj, liveobj); + Jim_SetResultString(interp, buf, -1); + return JIM_OK; + } + else if (option == OPT_OBJECTS) { + Jim_Obj *objPtr, *listObjPtr, *subListObjPtr; + + /* Count the number of live objects. */ + objPtr = interp->liveList; + listObjPtr = Jim_NewListObj(interp, NULL, 0); + while (objPtr) { + char buf[128]; + const char *type = objPtr->typePtr ? objPtr->typePtr->name : ""; + + subListObjPtr = Jim_NewListObj(interp, NULL, 0); + sprintf(buf, "%p", objPtr); + Jim_ListAppendElement(interp, subListObjPtr, Jim_NewStringObj(interp, buf, -1)); + Jim_ListAppendElement(interp, subListObjPtr, Jim_NewStringObj(interp, type, -1)); + Jim_ListAppendElement(interp, subListObjPtr, Jim_NewIntObj(interp, objPtr->refCount)); + Jim_ListAppendElement(interp, subListObjPtr, objPtr); + Jim_ListAppendElement(interp, listObjPtr, subListObjPtr); + objPtr = objPtr->nextObjPtr; + } + Jim_SetResult(interp, listObjPtr); + return JIM_OK; + } + else if (option == OPT_INVSTR) { + Jim_Obj *objPtr; + + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "object"); + return JIM_ERR; + } + objPtr = argv[2]; + if (objPtr->typePtr != NULL) + Jim_InvalidateStringRep(objPtr); + Jim_SetEmptyResult(interp); + return JIM_OK; + } + else if (option == OPT_SHOW) { + const char *s; + int len, charlen; + + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "object"); + return JIM_ERR; + } + s = Jim_GetString(argv[2], &len); +#ifdef JIM_UTF8 + charlen = utf8_strlen(s, len); +#else + charlen = len; +#endif + printf("refcount: %d, type: %s\n", argv[2]->refCount, JimObjTypeName(argv[2])); + printf("chars (%d): <<%s>>\n", charlen, s); + printf("bytes (%d):", len); + while (len--) { + printf(" %02x", (unsigned char)*s++); + } + printf("\n"); + return JIM_OK; + } + else if (option == OPT_SCRIPTLEN) { + ScriptObj *script; + + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "script"); + return JIM_ERR; + } + script = Jim_GetScript(interp, argv[2]); + Jim_SetResultInt(interp, script->len); + return JIM_OK; + } + else if (option == OPT_EXPRLEN) { + ExprByteCode *expr; + + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "expression"); + return JIM_ERR; + } + expr = JimGetExpression(interp, argv[2]); + if (expr == NULL) + return JIM_ERR; + Jim_SetResultInt(interp, expr->len); + return JIM_OK; + } + else if (option == OPT_EXPRBC) { + Jim_Obj *objPtr; + ExprByteCode *expr; + int i; + + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "expression"); + return JIM_ERR; + } + expr = JimGetExpression(interp, argv[2]); + if (expr == NULL) + return JIM_ERR; + objPtr = Jim_NewListObj(interp, NULL, 0); + for (i = 0; i < expr->len; i++) { + const char *type; + const Jim_ExprOperator *op; + Jim_Obj *obj = expr->token[i].objPtr; + + switch (expr->token[i].type) { + case JIM_TT_EXPR_INT: + type = "int"; + break; + case JIM_TT_EXPR_DOUBLE: + type = "double"; + break; + case JIM_TT_CMD: + type = "command"; + break; + case JIM_TT_VAR: + type = "variable"; + break; + case JIM_TT_DICTSUGAR: + type = "dictsugar"; + break; + case JIM_TT_EXPRSUGAR: + type = "exprsugar"; + break; + case JIM_TT_ESC: + type = "subst"; + break; + case JIM_TT_STR: + type = "string"; + break; + default: + op = JimExprOperatorInfoByOpcode(expr->token[i].type); + if (op == NULL) { + type = "private"; + } + else { + type = "operator"; + } + obj = Jim_NewStringObj(interp, op ? op->name : "", -1); + break; + } + Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, type, -1)); + Jim_ListAppendElement(interp, objPtr, obj); + } + Jim_SetResult(interp, objPtr); + return JIM_OK; + } + else { + Jim_SetResultString(interp, + "bad option. Valid options are refcount, " "objcount, objects, invstr", -1); + return JIM_ERR; + } + /* unreached */ +#endif /* JIM_BOOTSTRAP */ +#if !defined(JIM_DEBUG_COMMAND) + Jim_SetResultString(interp, "unsupported", -1); + return JIM_ERR; +#endif +} + +/* [eval] */ +static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int rc; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "script ?...?"); + return JIM_ERR; + } + + if (argc == 2) { + rc = Jim_EvalObj(interp, argv[1]); + } + else { + rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1)); + } + + if (rc == JIM_ERR) { + /* eval is "interesting", so add a stack frame here */ + interp->addStackTrace++; + } + return rc; +} + +/* [uplevel] */ +static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc >= 2) { + int retcode; + Jim_CallFrame *savedCallFrame, *targetCallFrame; + Jim_Obj *objPtr; + const char *str; + + /* Save the old callframe pointer */ + savedCallFrame = interp->framePtr; + + /* Lookup the target frame pointer */ + str = Jim_String(argv[1]); + if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') { + targetCallFrame =Jim_GetCallFrameByLevel(interp, argv[1]); + argc--; + argv++; + } + else { + targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL); + } + if (targetCallFrame == NULL) { + return JIM_ERR; + } + if (argc < 2) { + argv--; + Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?"); + return JIM_ERR; + } + /* Eval the code in the target callframe. */ + interp->framePtr = targetCallFrame; + if (argc == 2) { + retcode = Jim_EvalObj(interp, argv[1]); + } + else { + objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1); + Jim_IncrRefCount(objPtr); + retcode = Jim_EvalObj(interp, objPtr); + Jim_DecrRefCount(interp, objPtr); + } + interp->framePtr = savedCallFrame; + return retcode; + } + else { + Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?"); + return JIM_ERR; + } +} + +/* [expr] */ +static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *exprResultPtr; + int retcode; + + if (argc == 2) { + retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr); + } + else if (argc > 2) { + Jim_Obj *objPtr; + + objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1); + Jim_IncrRefCount(objPtr); + retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr); + Jim_DecrRefCount(interp, objPtr); + } + else { + Jim_WrongNumArgs(interp, 1, argv, "expression ?...?"); + return JIM_ERR; + } + if (retcode != JIM_OK) + return retcode; + Jim_SetResult(interp, exprResultPtr); + Jim_DecrRefCount(interp, exprResultPtr); + return JIM_OK; +} + +/* [break] */ +static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc != 1) { + Jim_WrongNumArgs(interp, 1, argv, ""); + return JIM_ERR; + } + return JIM_BREAK; +} + +/* [continue] */ +static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc != 1) { + Jim_WrongNumArgs(interp, 1, argv, ""); + return JIM_ERR; + } + return JIM_CONTINUE; +} + +/* [return] */ +static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int i; + Jim_Obj *stackTraceObj = NULL; + Jim_Obj *errorCodeObj = NULL; + int returnCode = JIM_OK; + long level = 1; + + for (i = 1; i < argc - 1; i += 2) { + if (Jim_CompareStringImmediate(interp, argv[i], "-code")) { + if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) { + return JIM_ERR; + } + } + else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) { + stackTraceObj = argv[i + 1]; + } + else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) { + errorCodeObj = argv[i + 1]; + } + else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) { + if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) { + Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]); + return JIM_ERR; + } + } + else { + break; + } + } + + if (i != argc - 1 && i != argc) { + Jim_WrongNumArgs(interp, 1, argv, + "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?"); + } + + /* If a stack trace is supplied and code is error, set the stack trace */ + if (stackTraceObj && returnCode == JIM_ERR) { + JimSetStackTrace(interp, stackTraceObj); + } + /* If an error code list is supplied, set the global $errorCode */ + if (errorCodeObj && returnCode == JIM_ERR) { + Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj); + } + interp->returnCode = returnCode; + interp->returnLevel = level; + + if (i == argc - 1) { + Jim_SetResult(interp, argv[i]); + } + return JIM_RETURN; +} + +/* [tailcall] */ +static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *objPtr; + + objPtr = Jim_NewListObj(interp, argv + 1, argc - 1); + Jim_SetResult(interp, objPtr); + return JIM_EVAL; +} + +/* [proc] */ +static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc != 4 && argc != 5) { + Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body"); + return JIM_ERR; + } + + if (argc == 4) { + return JimCreateProcedure(interp, argv[1], argv[2], NULL, argv[3]); + } + else { + return JimCreateProcedure(interp, argv[1], argv[2], argv[3], argv[4]); + } +} + +/* [local] */ +static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int retcode; + + /* Evaluate the arguments with 'local' in force */ + interp->local++; + retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1); + interp->local--; + + + /* If OK, and the result is a proc, add it to the list of local procs */ + if (retcode == 0) { + const char *procname = Jim_String(Jim_GetResult(interp)); + + if (Jim_FindHashEntry(&interp->commands, procname) == NULL) { + Jim_SetResultFormatted(interp, "not a proc: \"%s\"", procname); + return JIM_ERR; + } + if (interp->localProcs == NULL) { + interp->localProcs = Jim_Alloc(sizeof(*interp->localProcs)); + Jim_InitStack(interp->localProcs); + } + Jim_StackPush(interp->localProcs, Jim_StrDup(procname)); + } + + return retcode; +} + +/* [upcall] */ +static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?"); + return JIM_ERR; + } + else { + int retcode; + + Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG); + if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->u.proc.prevCmd) { + Jim_SetResultFormatted(interp, "no previous proc: \"%#s\"", argv[1]); + return JIM_ERR; + } + /* OK. Mark this command as being in an upcall */ + cmdPtr->u.proc.upcall++; + JimIncrCmdRefCount(cmdPtr); + + /* Invoke the command as normal */ + retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1); + + /* No longer in an upcall */ + cmdPtr->u.proc.upcall--; + JimDecrCmdRefCount(interp, cmdPtr); + + return retcode; + } +} + +/* [concat] */ +static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1)); + return JIM_OK; +} + +/* [upvar] */ +static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int i; + Jim_CallFrame *targetCallFrame; + + /* Lookup the target frame pointer */ + if (argc > 3 && (argc % 2 == 0)) { + targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]); + argc--; + argv++; + } + else { + targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL); + } + if (targetCallFrame == NULL) { + return JIM_ERR; + } + + /* Check for arity */ + if (argc < 3) { + Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?"); + return JIM_ERR; + } + + /* Now... for every other/local couple: */ + for (i = 1; i < argc; i += 2) { + if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK) + return JIM_ERR; + } + return JIM_OK; +} + +/* [global] */ +static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int i; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?"); + return JIM_ERR; + } + /* Link every var to the toplevel having the same name */ + if (interp->framePtr->level == 0) + return JIM_OK; /* global at toplevel... */ + for (i = 1; i < argc; i++) { + if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK) + return JIM_ERR; + } + return JIM_OK; +} + +/* does the [string map] operation. On error NULL is returned, + * otherwise a new string object with the result, having refcount = 0, + * is returned. */ +static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr, + Jim_Obj *objPtr, int nocase) +{ + int numMaps; + const char *str, *noMatchStart = NULL; + int strLen, i; + Jim_Obj *resultObjPtr; + + numMaps = Jim_ListLength(interp, mapListObjPtr); + if (numMaps % 2) { + Jim_SetResultString(interp, "list must contain an even number of elements", -1); + return NULL; + } + + str = Jim_String(objPtr); + strLen = Jim_Utf8Length(interp, objPtr); + + /* Map it */ + resultObjPtr = Jim_NewStringObj(interp, "", 0); + while (strLen) { + for (i = 0; i < numMaps; i += 2) { + Jim_Obj *objPtr; + const char *k; + int kl; + + Jim_ListIndex(interp, mapListObjPtr, i, &objPtr, JIM_NONE); + k = Jim_String(objPtr); + kl = Jim_Utf8Length(interp, objPtr); + + if (strLen >= kl && kl) { + int rc; + if (nocase) { + rc = JimStringCompareNoCase(str, k, kl); + } + else { + rc = JimStringCompare(str, kl, k, kl); + } + if (rc == 0) { + if (noMatchStart) { + Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart); + noMatchStart = NULL; + } + Jim_ListIndex(interp, mapListObjPtr, i + 1, &objPtr, JIM_NONE); + Jim_AppendObj(interp, resultObjPtr, objPtr); + str += utf8_index(str, kl); + strLen -= kl; + break; + } + } + } + if (i == numMaps) { /* no match */ + int c; + if (noMatchStart == NULL) + noMatchStart = str; + str += utf8_tounicode(str, &c); + strLen--; + } + } + if (noMatchStart) { + Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart); + } + return resultObjPtr; +} + +/* [string] */ +static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int len; + int opt_case = 1; + int option; + static const char * const options[] = { + "bytelength", "length", "compare", "match", "equal", "is", "byterange", "range", "map", + "repeat", "reverse", "index", "first", "last", + "trim", "trimleft", "trimright", "tolower", "toupper", NULL + }; + enum + { + OPT_BYTELENGTH, OPT_LENGTH, OPT_COMPARE, OPT_MATCH, OPT_EQUAL, OPT_IS, OPT_BYTERANGE, OPT_RANGE, OPT_MAP, + OPT_REPEAT, OPT_REVERSE, OPT_INDEX, OPT_FIRST, OPT_LAST, + OPT_TRIM, OPT_TRIMLEFT, OPT_TRIMRIGHT, OPT_TOLOWER, OPT_TOUPPER + }; + static const char * const nocase_options[] = { + "-nocase", NULL + }; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "option ?arguments ...?"); + return JIM_ERR; + } + if (Jim_GetEnum(interp, argv[1], options, &option, NULL, + JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) + return JIM_ERR; + + switch (option) { + case OPT_LENGTH: + case OPT_BYTELENGTH: + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "string"); + return JIM_ERR; + } + if (option == OPT_LENGTH) { + len = Jim_Utf8Length(interp, argv[2]); + } + else { + len = Jim_Length(argv[2]); + } + Jim_SetResultInt(interp, len); + return JIM_OK; + + case OPT_COMPARE: + case OPT_EQUAL: + if (argc != 4 && + (argc != 5 || + Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL, + JIM_ENUM_ABBREV) != JIM_OK)) { + Jim_WrongNumArgs(interp, 2, argv, "?-nocase? string1 string2"); + return JIM_ERR; + } + if (opt_case == 0) { + argv++; + } + if (option == OPT_COMPARE || !opt_case) { + Jim_SetResultInt(interp, Jim_StringCompareObj(interp, argv[2], argv[3], !opt_case)); + } + else { + Jim_SetResultBool(interp, Jim_StringEqObj(argv[2], argv[3])); + } + return JIM_OK; + + case OPT_MATCH: + if (argc != 4 && + (argc != 5 || + Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL, + JIM_ENUM_ABBREV) != JIM_OK)) { + Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string"); + return JIM_ERR; + } + if (opt_case == 0) { + argv++; + } + Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case)); + return JIM_OK; + + case OPT_MAP:{ + Jim_Obj *objPtr; + + if (argc != 4 && + (argc != 5 || + Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL, + JIM_ENUM_ABBREV) != JIM_OK)) { + Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string"); + return JIM_ERR; + } + + if (opt_case == 0) { + argv++; + } + objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case); + if (objPtr == NULL) { + return JIM_ERR; + } + Jim_SetResult(interp, objPtr); + return JIM_OK; + } + + case OPT_RANGE: + case OPT_BYTERANGE:{ + Jim_Obj *objPtr; + + if (argc != 5) { + Jim_WrongNumArgs(interp, 2, argv, "string first last"); + return JIM_ERR; + } + if (option == OPT_RANGE) { + objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]); + } + else + { + objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]); + } + + if (objPtr == NULL) { + return JIM_ERR; + } + Jim_SetResult(interp, objPtr); + return JIM_OK; + } + + case OPT_REPEAT:{ + Jim_Obj *objPtr; + jim_wide count; + + if (argc != 4) { + Jim_WrongNumArgs(interp, 2, argv, "string count"); + return JIM_ERR; + } + if (Jim_GetWide(interp, argv[3], &count) != JIM_OK) { + return JIM_ERR; + } + objPtr = Jim_NewStringObj(interp, "", 0); + if (count > 0) { + while (count--) { + Jim_AppendObj(interp, objPtr, argv[2]); + } + } + Jim_SetResult(interp, objPtr); + return JIM_OK; + } + + case OPT_REVERSE:{ + char *buf, *p; + const char *str; + int len; + int i; + + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "string"); + return JIM_ERR; + } + + str = Jim_GetString(argv[2], &len); + buf = Jim_Alloc(len + 1); + p = buf + len; + *p = 0; + for (i = 0; i < len; ) { + int c; + int l = utf8_tounicode(str, &c); + memcpy(p - l, str, l); + p -= l; + i += l; + str += l; + } + Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len)); + return JIM_OK; + } + + case OPT_INDEX:{ + int idx; + const char *str; + + if (argc != 4) { + Jim_WrongNumArgs(interp, 2, argv, "string index"); + return JIM_ERR; + } + if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) { + return JIM_ERR; + } + str = Jim_String(argv[2]); + len = Jim_Utf8Length(interp, argv[2]); + if (idx != INT_MIN && idx != INT_MAX) { + idx = JimRelToAbsIndex(len, idx); + } + if (idx < 0 || idx >= len || str == NULL) { + Jim_SetResultString(interp, "", 0); + } + else if (len == Jim_Length(argv[2])) { + /* ASCII optimisation */ + Jim_SetResultString(interp, str + idx, 1); + } + else { + int c; + int i = utf8_index(str, idx); + Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c)); + } + return JIM_OK; + } + + case OPT_FIRST: + case OPT_LAST:{ + int idx = 0, l1, l2; + const char *s1, *s2; + + if (argc != 4 && argc != 5) { + Jim_WrongNumArgs(interp, 2, argv, "subString string ?index?"); + return JIM_ERR; + } + s1 = Jim_String(argv[2]); + s2 = Jim_String(argv[3]); + l1 = Jim_Utf8Length(interp, argv[2]); + l2 = Jim_Utf8Length(interp, argv[3]); + if (argc == 5) { + if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) { + return JIM_ERR; + } + idx = JimRelToAbsIndex(l2, idx); + } + else if (option == OPT_LAST) { + idx = l2; + } + if (option == OPT_FIRST) { + Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx)); + } + else { +#ifdef JIM_UTF8 + Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx)); +#else + Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx)); +#endif + } + return JIM_OK; + } + + case OPT_TRIM: + case OPT_TRIMLEFT: + case OPT_TRIMRIGHT:{ + Jim_Obj *trimchars; + + if (argc != 3 && argc != 4) { + Jim_WrongNumArgs(interp, 2, argv, "string ?trimchars?"); + return JIM_ERR; + } + trimchars = (argc == 4 ? argv[3] : NULL); + if (option == OPT_TRIM) { + Jim_SetResult(interp, JimStringTrim(interp, argv[2], trimchars)); + } + else if (option == OPT_TRIMLEFT) { + Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], trimchars)); + } + else if (option == OPT_TRIMRIGHT) { + Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], trimchars)); + } + return JIM_OK; + } + + case OPT_TOLOWER: + case OPT_TOUPPER: + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "string"); + return JIM_ERR; + } + if (option == OPT_TOLOWER) { + Jim_SetResult(interp, JimStringToLower(interp, argv[2])); + } + else { + Jim_SetResult(interp, JimStringToUpper(interp, argv[2])); + } + return JIM_OK; + + case OPT_IS: + if (argc == 4 || (argc == 5 && Jim_CompareStringImmediate(interp, argv[3], "-strict"))) { + return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5); + } + Jim_WrongNumArgs(interp, 2, argv, "class ?-strict? str"); + return JIM_ERR; + } + return JIM_OK; +} + +/* [time] */ +static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + long i, count = 1; + jim_wide start, elapsed; + char buf[60]; + const char *fmt = "%" JIM_WIDE_MODIFIER " microseconds per iteration"; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "script ?count?"); + return JIM_ERR; + } + if (argc == 3) { + if (Jim_GetLong(interp, argv[2], &count) != JIM_OK) + return JIM_ERR; + } + if (count < 0) + return JIM_OK; + i = count; + start = JimClock(); + while (i-- > 0) { + int retval; + + retval = Jim_EvalObj(interp, argv[1]); + if (retval != JIM_OK) { + return retval; + } + } + elapsed = JimClock() - start; + sprintf(buf, fmt, count == 0 ? 0 : elapsed / count); + Jim_SetResultString(interp, buf, -1); + return JIM_OK; +} + +/* [exit] */ +static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + long exitCode = 0; + + if (argc > 2) { + Jim_WrongNumArgs(interp, 1, argv, "?exitCode?"); + return JIM_ERR; + } + if (argc == 2) { + if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK) + return JIM_ERR; + } + interp->exitCode = exitCode; + return JIM_EXIT; +} + +/* [catch] */ +static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int exitCode = 0; + int i; + int sig = 0; + + /* Which return codes are ignored (passed through)? By default, only exit, eval and signal */ + jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL); + static const int max_ignore_code = sizeof(ignore_mask) * 8; + + /* Reset the error code before catch. + * Note that this is not strictly correct. + */ + Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1)); + + for (i = 1; i < argc - 1; i++) { + const char *arg = Jim_String(argv[i]); + jim_wide option; + int ignore; + + /* It's a pity we can't use Jim_GetEnum here :-( */ + if (strcmp(arg, "--") == 0) { + i++; + break; + } + if (*arg != '-') { + break; + } + + if (strncmp(arg, "-no", 3) == 0) { + arg += 3; + ignore = 1; + } + else { + arg++; + ignore = 0; + } + + if (Jim_StringToWide(arg, &option, 10) != JIM_OK) { + option = -1; + } + if (option < 0) { + option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize); + } + if (option < 0) { + goto wrongargs; + } + + if (ignore) { + ignore_mask |= (1 << option); + } + else { + ignore_mask &= ~(1 << option); + } + } + + argc -= i; + if (argc < 1 || argc > 3) { + wrongargs: + Jim_WrongNumArgs(interp, 1, argv, + "?-?no?code ... --? script ?resultVarName? ?optionVarName?"); + return JIM_ERR; + } + argv += i; + + if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) { + sig++; + } + + interp->signal_level += sig; + if (interp->signal_level && interp->sigmask) { + /* If a signal is set, don't even try to execute the body */ + exitCode = JIM_SIGNAL; + } + else { + exitCode = Jim_EvalObj(interp, argv[0]); + } + interp->signal_level -= sig; + + /* Catch or pass through? Only the first 32/64 codes can be passed through */ + if (exitCode >= 0 && exitCode < max_ignore_code && ((1 << exitCode) & ignore_mask)) { + /* Not caught, pass it up */ + return exitCode; + } + + if (sig && exitCode == JIM_SIGNAL) { + /* Catch the signal at this level */ + if (interp->signal_set_result) { + interp->signal_set_result(interp, interp->sigmask); + } + else { + Jim_SetResultInt(interp, interp->sigmask); + } + interp->sigmask = 0; + } + + if (argc >= 2) { + if (Jim_SetVariable(interp, argv[1], Jim_GetResult(interp)) != JIM_OK) { + return JIM_ERR; + } + if (argc == 3) { + Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0); + + Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1)); + Jim_ListAppendElement(interp, optListObj, + Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode)); + Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1)); + Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel)); + if (exitCode == JIM_ERR) { + Jim_Obj *errorCode; + Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo", + -1)); + Jim_ListAppendElement(interp, optListObj, interp->stackTrace); + + errorCode = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE); + if (errorCode) { + Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1)); + Jim_ListAppendElement(interp, optListObj, errorCode); + } + } + if (Jim_SetVariable(interp, argv[2], optListObj) != JIM_OK) { + return JIM_ERR; + } + } + } + Jim_SetResultInt(interp, exitCode); + return JIM_OK; +} + +#ifdef JIM_REFERENCES + +/* [ref] */ +static int Jim_RefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc != 3 && argc != 4) { + Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?"); + return JIM_ERR; + } + if (argc == 3) { + Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], NULL)); + } + else { + Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], argv[3])); + } + return JIM_OK; +} + +/* [getref] */ +static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Reference *refPtr; + + if (argc != 2) { + Jim_WrongNumArgs(interp, 1, argv, "reference"); + return JIM_ERR; + } + if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL) + return JIM_ERR; + Jim_SetResult(interp, refPtr->objPtr); + return JIM_OK; +} + +/* [setref] */ +static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Reference *refPtr; + + if (argc != 3) { + Jim_WrongNumArgs(interp, 1, argv, "reference newValue"); + return JIM_ERR; + } + if ((refPtr = Jim_GetReference(interp, argv[1])) == NULL) + return JIM_ERR; + Jim_IncrRefCount(argv[2]); + Jim_DecrRefCount(interp, refPtr->objPtr); + refPtr->objPtr = argv[2]; + Jim_SetResult(interp, argv[2]); + return JIM_OK; +} + +/* [collect] */ +static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc != 1) { + Jim_WrongNumArgs(interp, 1, argv, ""); + return JIM_ERR; + } + Jim_SetResultInt(interp, Jim_Collect(interp)); + + /* Free all the freed objects. */ + while (interp->freeList) { + Jim_Obj *nextObjPtr = interp->freeList->nextObjPtr; + Jim_Free(interp->freeList); + interp->freeList = nextObjPtr; + } + + return JIM_OK; +} + +/* [finalize] reference ?newValue? */ +static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc != 2 && argc != 3) { + Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?"); + return JIM_ERR; + } + if (argc == 2) { + Jim_Obj *cmdNamePtr; + + if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK) + return JIM_ERR; + if (cmdNamePtr != NULL) /* otherwise the null string is returned. */ + Jim_SetResult(interp, cmdNamePtr); + } + else { + if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK) + return JIM_ERR; + Jim_SetResult(interp, argv[2]); + } + return JIM_OK; +} + +/* [info references] */ +static int JimInfoReferences(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *listObjPtr; + Jim_HashTableIterator *htiter; + Jim_HashEntry *he; + + listObjPtr = Jim_NewListObj(interp, NULL, 0); + + htiter = Jim_GetHashTableIterator(&interp->references); + while ((he = Jim_NextHashEntry(htiter)) != NULL) { + char buf[JIM_REFERENCE_SPACE]; + Jim_Reference *refPtr = he->u.val; + const jim_wide *refId = he->key; + + JimFormatReference(buf, refPtr, *refId); + Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, buf, -1)); + } + Jim_FreeHashTableIterator(htiter); + Jim_SetResult(interp, listObjPtr); + return JIM_OK; +} +#endif + +/* [rename] */ +static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *oldName, *newName; + + if (argc != 3) { + Jim_WrongNumArgs(interp, 1, argv, "oldName newName"); + return JIM_ERR; + } + + if (JimValidName(interp, "new procedure", argv[2])) { + return JIM_ERR; + } + + oldName = Jim_String(argv[1]); + newName = Jim_String(argv[2]); + return Jim_RenameCommand(interp, oldName, newName); +} + +int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj) +{ + int i; + int len; + Jim_Obj *resultObj; + Jim_Obj *dictObj; + Jim_Obj **dictValuesObj; + + if (Jim_DictKeysVector(interp, objPtr, NULL, 0, &dictObj, JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; + } + + /* XXX: Could make the exact-match case much more efficient here. + * See JimCommandsList() + */ + if (Jim_DictPairs(interp, dictObj, &dictValuesObj, &len) != JIM_OK) { + return JIM_ERR; + } + + /* Only return the matching values */ + resultObj = Jim_NewListObj(interp, NULL, 0); + + for (i = 0; i < len; i += 2) { + if (patternObj == NULL || Jim_StringMatchObj(interp, patternObj, dictValuesObj[i], 0)) { + Jim_ListAppendElement(interp, resultObj, dictValuesObj[i]); + } + } + Jim_Free(dictValuesObj); + + Jim_SetResult(interp, resultObj); + return JIM_OK; +} + +int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr) +{ + if (SetDictFromAny(interp, objPtr) != JIM_OK) { + return -1; + } + return ((Jim_HashTable *)objPtr->internalRep.ptr)->used; +} + +/* [dict] */ +static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *objPtr; + int option; + static const char * const options[] = { + "create", "get", "set", "unset", "exists", "keys", "merge", "size", "with", NULL + }; + enum + { + OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST, OPT_KEYS, OPT_MERGE, OPT_SIZE, OPT_WITH, + }; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "subcommand ?arguments ...?"); + return JIM_ERR; + } + + if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; + } + + switch (option) { + case OPT_GET: + if (argc < 3) { + Jim_WrongNumArgs(interp, 2, argv, "varName ?key ...?"); + return JIM_ERR; + } + if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, + JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; + } + Jim_SetResult(interp, objPtr); + return JIM_OK; + + case OPT_SET: + if (argc < 5) { + Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value"); + return JIM_ERR; + } + return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG); + + case OPT_EXIST: + if (argc < 3) { + Jim_WrongNumArgs(interp, 2, argv, "varName ?key ...?"); + return JIM_ERR; + } + Jim_SetResultBool(interp, Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, + &objPtr, JIM_ERRMSG) == JIM_OK); + return JIM_OK; + + case OPT_UNSET: + if (argc < 4) { + Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?"); + return JIM_ERR; + } + return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_NONE); + + case OPT_KEYS: + if (argc != 3 && argc != 4) { + Jim_WrongNumArgs(interp, 2, argv, "dictVar ?pattern?"); + return JIM_ERR; + } + return Jim_DictKeys(interp, argv[2], argc == 4 ? argv[3] : NULL); + + case OPT_SIZE: { + int size; + + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "dictVar"); + return JIM_ERR; + } + + size = Jim_DictSize(interp, argv[2]); + if (size < 0) { + return JIM_ERR; + } + Jim_SetResultInt(interp, size); + return JIM_OK; + } + + case OPT_MERGE: + if (argc == 2) { + return JIM_OK; + } + else if (argv[2]->typePtr != &dictObjType && SetDictFromAny(interp, argv[2]) != JIM_OK) { + return JIM_ERR; + } + else { + return Jim_EvalPrefix(interp, "dict merge", argc - 2, argv + 2); + } + + case OPT_WITH: + if (argc < 4) { + Jim_WrongNumArgs(interp, 2, argv, "dictVar ?key ...? script"); + return JIM_ERR; + } + else if (Jim_GetVariable(interp, argv[2], JIM_ERRMSG) == NULL) { + return JIM_ERR; + } + else { + return Jim_EvalPrefix(interp, "dict with", argc - 2, argv + 2); + } + + case OPT_CREATE: + if (argc % 2) { + Jim_WrongNumArgs(interp, 2, argv, "?key value ...?"); + return JIM_ERR; + } + objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2); + Jim_SetResult(interp, objPtr); + return JIM_OK; + + default: + abort(); + } +} + +/* [subst] */ +static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + static const char * const options[] = { + "-nobackslashes", "-nocommands", "-novariables", NULL + }; + enum + { OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES }; + int i; + int flags = JIM_SUBST_FLAG; + Jim_Obj *objPtr; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "?options? string"); + return JIM_ERR; + } + for (i = 1; i < (argc - 1); i++) { + int option; + + if (Jim_GetEnum(interp, argv[i], options, &option, NULL, + JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { + return JIM_ERR; + } + switch (option) { + case OPT_NOBACKSLASHES: + flags |= JIM_SUBST_NOESC; + break; + case OPT_NOCOMMANDS: + flags |= JIM_SUBST_NOCMD; + break; + case OPT_NOVARIABLES: + flags |= JIM_SUBST_NOVAR; + break; + } + } + if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) { + return JIM_ERR; + } + Jim_SetResult(interp, objPtr); + return JIM_OK; +} + +/* [info] */ +static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int cmd; + Jim_Obj *objPtr; + int mode = 0; + + static const char * const commands[] = { + "body", "commands", "procs", "channels", "exists", "globals", "level", "frame", "locals", + "vars", "version", "patchlevel", "complete", "args", "hostname", + "script", "source", "stacktrace", "nameofexecutable", "returncodes", + "references", NULL + }; + enum + { INFO_BODY, INFO_COMMANDS, INFO_PROCS, INFO_CHANNELS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL, + INFO_FRAME, INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_PATCHLEVEL, INFO_COMPLETE, INFO_ARGS, + INFO_HOSTNAME, INFO_SCRIPT, INFO_SOURCE, INFO_STACKTRACE, INFO_NAMEOFEXECUTABLE, + INFO_RETURNCODES, INFO_REFERENCES, + }; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "subcommand ?args ...?"); + return JIM_ERR; + } + if (Jim_GetEnum(interp, argv[1], commands, &cmd, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV) + != JIM_OK) { + return JIM_ERR; + } + + /* Test for the the most common commands first, just in case it makes a difference */ + switch (cmd) { + case INFO_EXISTS:{ + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "varName"); + return JIM_ERR; + } + Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL); + break; + } + + case INFO_CHANNELS: +#ifndef jim_ext_aio + Jim_SetResultString(interp, "aio not enabled", -1); + return JIM_ERR; +#endif + case INFO_COMMANDS: + case INFO_PROCS: + if (argc != 2 && argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "?pattern?"); + return JIM_ERR; + } + Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, + (cmd - INFO_COMMANDS))); + break; + + case INFO_VARS: + mode++; /* JIM_VARLIST_VARS */ + case INFO_LOCALS: + mode++; /* JIM_VARLIST_LOCALS */ + case INFO_GLOBALS: + /* mode 0 => JIM_VARLIST_GLOBALS */ + if (argc != 2 && argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "?pattern?"); + return JIM_ERR; + } + Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode)); + break; + + case INFO_SCRIPT: + if (argc != 2) { + Jim_WrongNumArgs(interp, 2, argv, ""); + return JIM_ERR; + } + Jim_SetResult(interp, Jim_GetScript(interp, interp->currentScriptObj)->fileNameObj); + break; + + case INFO_SOURCE:{ + int line; + Jim_Obj *resObjPtr; + Jim_Obj *fileNameObj; + + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "source"); + return JIM_ERR; + } + if (argv[2]->typePtr == &sourceObjType) { + fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj; + line = argv[2]->internalRep.sourceValue.lineNumber; + } + else if (argv[2]->typePtr == &scriptObjType) { + ScriptObj *script = Jim_GetScript(interp, argv[2]); + fileNameObj = script->fileNameObj; + line = script->line; + } + else { + fileNameObj = interp->emptyObj; + line = 1; + } + resObjPtr = Jim_NewListObj(interp, NULL, 0); + Jim_ListAppendElement(interp, resObjPtr, fileNameObj); + Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line)); + Jim_SetResult(interp, resObjPtr); + break; + } + + case INFO_STACKTRACE: + Jim_SetResult(interp, interp->stackTrace); + break; + + case INFO_LEVEL: + case INFO_FRAME: + switch (argc) { + case 2: + Jim_SetResultInt(interp, interp->framePtr->level); + break; + + case 3: + if (JimInfoLevel(interp, argv[2], &objPtr, cmd == INFO_LEVEL) != JIM_OK) { + return JIM_ERR; + } + Jim_SetResult(interp, objPtr); + break; + + default: + Jim_WrongNumArgs(interp, 2, argv, "?levelNum?"); + return JIM_ERR; + } + break; + + case INFO_BODY: + case INFO_ARGS:{ + Jim_Cmd *cmdPtr; + + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "procname"); + return JIM_ERR; + } + if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) { + return JIM_ERR; + } + if (!cmdPtr->isproc) { + Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]); + return JIM_ERR; + } + Jim_SetResult(interp, + cmd == INFO_BODY ? cmdPtr->u.proc.bodyObjPtr : cmdPtr->u.proc.argListObjPtr); + break; + } + + case INFO_VERSION: + case INFO_PATCHLEVEL:{ + char buf[(JIM_INTEGER_SPACE * 2) + 1]; + + sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100); + Jim_SetResultString(interp, buf, -1); + break; + } + + case INFO_COMPLETE: + if (argc != 3 && argc != 4) { + Jim_WrongNumArgs(interp, 2, argv, "script ?missing?"); + return JIM_ERR; + } + else { + int len; + const char *s = Jim_GetString(argv[2], &len); + char missing; + + Jim_SetResultBool(interp, Jim_ScriptIsComplete(s, len, &missing)); + if (missing != ' ' && argc == 4) { + Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1)); + } + } + break; + + case INFO_HOSTNAME: + /* Redirect to os.gethostname if it exists */ + return Jim_Eval(interp, "os.gethostname"); + + case INFO_NAMEOFEXECUTABLE: + /* Redirect to Tcl proc */ + return Jim_Eval(interp, "{info nameofexecutable}"); + + case INFO_RETURNCODES: + if (argc == 2) { + int i; + Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); + + for (i = 0; jimReturnCodes[i]; i++) { + Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i)); + Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, + jimReturnCodes[i], -1)); + } + + Jim_SetResult(interp, listObjPtr); + } + else if (argc == 3) { + long code; + const char *name; + + if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) { + return JIM_ERR; + } + name = Jim_ReturnCode(code); + if (*name == '?') { + Jim_SetResultInt(interp, code); + } + else { + Jim_SetResultString(interp, name, -1); + } + } + else { + Jim_WrongNumArgs(interp, 2, argv, "?code?"); + return JIM_ERR; + } + break; + case INFO_REFERENCES: +#ifdef JIM_REFERENCES + return JimInfoReferences(interp, argc, argv); +#else + Jim_SetResultString(interp, "not supported", -1); + return JIM_ERR; +#endif + } + return JIM_OK; +} + +/* [exists] */ +static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *objPtr; + + static const char * const options[] = { + "-command", "-proc", "-var", NULL + }; + enum + { + OPT_COMMAND, OPT_PROC, OPT_VAR + }; + int option; + + if (argc == 2) { + option = OPT_VAR; + objPtr = argv[1]; + } + else if (argc == 3) { + if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { + return JIM_ERR; + } + objPtr = argv[2]; + } + else { + Jim_WrongNumArgs(interp, 1, argv, "?option? name"); + return JIM_ERR; + } + + /* Test for the the most common commands first, just in case it makes a difference */ + switch (option) { + case OPT_VAR: + Jim_SetResultBool(interp, Jim_GetVariable(interp, objPtr, 0) != NULL); + break; + + case OPT_COMMAND: + case OPT_PROC: { + Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE); + Jim_SetResultBool(interp, cmd != NULL && (option == OPT_COMMAND || cmd->isproc)); + break; + } + } + return JIM_OK; +} + +/* [split] */ +static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *str, *splitChars, *noMatchStart; + int splitLen, strLen; + Jim_Obj *resObjPtr; + int c; + int len; + + if (argc != 2 && argc != 3) { + Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?"); + return JIM_ERR; + } + + str = Jim_GetString(argv[1], &len); + if (len == 0) { + return JIM_OK; + } + strLen = Jim_Utf8Length(interp, argv[1]); + + /* Init */ + if (argc == 2) { + splitChars = " \n\t\r"; + splitLen = 4; + } + else { + splitChars = Jim_String(argv[2]); + splitLen = Jim_Utf8Length(interp, argv[2]); + } + + noMatchStart = str; + resObjPtr = Jim_NewListObj(interp, NULL, 0); + + /* Split */ + if (splitLen) { + Jim_Obj *objPtr; + while (strLen--) { + const char *sc = splitChars; + int scLen = splitLen; + int sl = utf8_tounicode(str, &c); + while (scLen--) { + int pc; + sc += utf8_tounicode(sc, &pc); + if (c == pc) { + objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart)); + Jim_ListAppendElement(interp, resObjPtr, objPtr); + noMatchStart = str + sl; + break; + } + } + str += sl; + } + objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart)); + Jim_ListAppendElement(interp, resObjPtr, objPtr); + } + else { + /* This handles the special case of splitchars eq {} + * Optimise by sharing common (ASCII) characters + */ + Jim_Obj **commonObj = NULL; +#define NUM_COMMON (128 - 9) + while (strLen--) { + int n = utf8_tounicode(str, &c); +#ifdef JIM_OPTIMIZATION + if (c >= 9 && c < 128) { + /* Common ASCII char. Note that 9 is the tab character */ + c -= 9; + if (!commonObj) { + commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON); + memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON); + } + if (!commonObj[c]) { + commonObj[c] = Jim_NewStringObj(interp, str, 1); + } + Jim_ListAppendElement(interp, resObjPtr, commonObj[c]); + str++; + continue; + } +#endif + Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1)); + str += n; + } + Jim_Free(commonObj); + } + + Jim_SetResult(interp, resObjPtr); + return JIM_OK; +} + +/* [join] */ +static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *joinStr; + int joinStrLen, i, listLen; + Jim_Obj *resObjPtr; + + if (argc != 2 && argc != 3) { + Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?"); + return JIM_ERR; + } + /* Init */ + if (argc == 2) { + joinStr = " "; + joinStrLen = 1; + } + else { + joinStr = Jim_GetString(argv[2], &joinStrLen); + } + listLen = Jim_ListLength(interp, argv[1]); + resObjPtr = Jim_NewStringObj(interp, NULL, 0); + /* Split */ + for (i = 0; i < listLen; i++) { + Jim_Obj *objPtr = 0; + + Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE); + Jim_AppendObj(interp, resObjPtr, objPtr); + if (i + 1 != listLen) { + Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen); + } + } + Jim_SetResult(interp, resObjPtr); + return JIM_OK; +} + +/* [format] */ +static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *objPtr; + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?"); + return JIM_ERR; + } + objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2); + if (objPtr == NULL) + return JIM_ERR; + Jim_SetResult(interp, objPtr); + return JIM_OK; +} + +/* [scan] */ +static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *listPtr, **outVec; + int outc, i; + + if (argc < 3) { + Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?"); + return JIM_ERR; + } + if (argv[2]->typePtr != &scanFmtStringObjType) + SetScanFmtFromAny(interp, argv[2]); + if (FormatGetError(argv[2]) != 0) { + Jim_SetResultString(interp, FormatGetError(argv[2]), -1); + return JIM_ERR; + } + if (argc > 3) { + int maxPos = FormatGetMaxPos(argv[2]); + int count = FormatGetCnvCount(argv[2]); + + if (maxPos > argc - 3) { + Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1); + return JIM_ERR; + } + else if (count > argc - 3) { + Jim_SetResultString(interp, "different numbers of variable names and " + "field specifiers", -1); + return JIM_ERR; + } + else if (count < argc - 3) { + Jim_SetResultString(interp, "variable is not assigned by any " + "conversion specifiers", -1); + return JIM_ERR; + } + } + listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG); + if (listPtr == 0) + return JIM_ERR; + if (argc > 3) { + int rc = JIM_OK; + int count = 0; + + if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) { + int len = Jim_ListLength(interp, listPtr); + + if (len != 0) { + JimListGetElements(interp, listPtr, &outc, &outVec); + for (i = 0; i < outc; ++i) { + if (Jim_Length(outVec[i]) > 0) { + ++count; + if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) { + rc = JIM_ERR; + } + } + } + } + Jim_FreeNewObj(interp, listPtr); + } + else { + count = -1; + } + if (rc == JIM_OK) { + Jim_SetResultInt(interp, count); + } + return rc; + } + else { + if (listPtr == (Jim_Obj *)EOF) { + Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0)); + return JIM_OK; + } + Jim_SetResult(interp, listPtr); + } + return JIM_OK; +} + +/* [error] */ +static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if (argc != 2 && argc != 3) { + Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?"); + return JIM_ERR; + } + Jim_SetResult(interp, argv[1]); + if (argc == 3) { + JimSetStackTrace(interp, argv[2]); + return JIM_ERR; + } + interp->addStackTrace++; + return JIM_ERR; +} + +/* [lrange] */ +static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *objPtr; + + if (argc != 4) { + Jim_WrongNumArgs(interp, 1, argv, "list first last"); + return JIM_ERR; + } + if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL) + return JIM_ERR; + Jim_SetResult(interp, objPtr); + return JIM_OK; +} + +/* [lrepeat] */ +static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *objPtr; + long count; + + if (argc < 2 || Jim_GetLong(interp, argv[1], &count) != JIM_OK || count < 0) { + Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?"); + return JIM_ERR; + } + + if (count == 0 || argc == 2) { + return JIM_OK; + } + + argc -= 2; + argv += 2; + + objPtr = Jim_NewListObj(interp, argv, argc); + while (--count) { + int i; + + for (i = 0; i < argc; i++) { + ListAppendElement(objPtr, argv[i]); + } + } + + Jim_SetResult(interp, objPtr); + return JIM_OK; +} + +char **Jim_GetEnviron(void) +{ +#if defined(HAVE__NSGETENVIRON) + return *_NSGetEnviron(); +#else + #if !defined(NO_ENVIRON_EXTERN) + extern char **environ; + #endif + + return environ; +#endif +} + +void Jim_SetEnviron(char **env) +{ +#if defined(HAVE__NSGETENVIRON) + *_NSGetEnviron() = env; +#else + #if !defined(NO_ENVIRON_EXTERN) + extern char **environ; + #endif + + environ = env; +#endif +} + +/* [env] */ +static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *key; + const char *val; + + if (argc == 1) { + char **e = Jim_GetEnviron(); + + int i; + Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); + + for (i = 0; e[i]; i++) { + const char *equals = strchr(e[i], '='); + + if (equals) { + Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i], + equals - e[i])); + Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1)); + } + } + + Jim_SetResult(interp, listObjPtr); + return JIM_OK; + } + + if (argc < 2) { + Jim_WrongNumArgs(interp, 1, argv, "varName ?default?"); + return JIM_ERR; + } + key = Jim_String(argv[1]); + val = getenv(key); + if (val == NULL) { + if (argc < 3) { + Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]); + return JIM_ERR; + } + val = Jim_String(argv[2]); + } + Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1)); + return JIM_OK; +} + +/* [source] */ +static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int retval; + + if (argc != 2) { + Jim_WrongNumArgs(interp, 1, argv, "fileName"); + return JIM_ERR; + } + retval = Jim_EvalFile(interp, Jim_String(argv[1])); + if (retval == JIM_RETURN) + return JIM_OK; + return retval; +} + +/* [lreverse] */ +static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *revObjPtr, **ele; + int len; + + if (argc != 2) { + Jim_WrongNumArgs(interp, 1, argv, "list"); + return JIM_ERR; + } + JimListGetElements(interp, argv[1], &len, &ele); + len--; + revObjPtr = Jim_NewListObj(interp, NULL, 0); + while (len >= 0) + ListAppendElement(revObjPtr, ele[len--]); + Jim_SetResult(interp, revObjPtr); + return JIM_OK; +} + +static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step) +{ + jim_wide len; + + if (step == 0) + return -1; + if (start == end) + return 0; + else if (step > 0 && start > end) + return -1; + else if (step < 0 && end > start) + return -1; + len = end - start; + if (len < 0) + len = -len; /* abs(len) */ + if (step < 0) + step = -step; /* abs(step) */ + len = 1 + ((len - 1) / step); + /* We can truncate safely to INT_MAX, the range command + * will always return an error for a such long range + * because Tcl lists can't be so long. */ + if (len > INT_MAX) + len = INT_MAX; + return (int)((len < 0) ? -1 : len); +} + +/* [range] */ +static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + jim_wide start = 0, end, step = 1; + int len, i; + Jim_Obj *objPtr; + + if (argc < 2 || argc > 4) { + Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?"); + return JIM_ERR; + } + if (argc == 2) { + if (Jim_GetWide(interp, argv[1], &end) != JIM_OK) + return JIM_ERR; + } + else { + if (Jim_GetWide(interp, argv[1], &start) != JIM_OK || + Jim_GetWide(interp, argv[2], &end) != JIM_OK) + return JIM_ERR; + if (argc == 4 && Jim_GetWide(interp, argv[3], &step) != JIM_OK) + return JIM_ERR; + } + if ((len = JimRangeLen(start, end, step)) == -1) { + Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1); + return JIM_ERR; + } + objPtr = Jim_NewListObj(interp, NULL, 0); + for (i = 0; i < len; i++) + ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step)); + Jim_SetResult(interp, objPtr); + return JIM_OK; +} + +/* [rand] */ +static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + jim_wide min = 0, max = 0, len, maxMul; + + if (argc < 1 || argc > 3) { + Jim_WrongNumArgs(interp, 1, argv, "?min? max"); + return JIM_ERR; + } + if (argc == 1) { + max = JIM_WIDE_MAX; + } else if (argc == 2) { + if (Jim_GetWide(interp, argv[1], &max) != JIM_OK) + return JIM_ERR; + } else if (argc == 3) { + if (Jim_GetWide(interp, argv[1], &min) != JIM_OK || + Jim_GetWide(interp, argv[2], &max) != JIM_OK) + return JIM_ERR; + } + len = max-min; + if (len < 0) { + Jim_SetResultString(interp, "Invalid arguments (max < min)", -1); + return JIM_ERR; + } + maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0); + while (1) { + jim_wide r; + + JimRandomBytes(interp, &r, sizeof(jim_wide)); + if (r < 0 || r >= maxMul) continue; + r = (len == 0) ? 0 : r%len; + Jim_SetResultInt(interp, min+r); + return JIM_OK; + } +} + +static const struct { + const char *name; + Jim_CmdProc cmdProc; +} Jim_CoreCommandsTable[] = { + {"set", Jim_SetCoreCommand}, + {"unset", Jim_UnsetCoreCommand}, + {"puts", Jim_PutsCoreCommand}, + {"+", Jim_AddCoreCommand}, + {"*", Jim_MulCoreCommand}, + {"-", Jim_SubCoreCommand}, + {"/", Jim_DivCoreCommand}, + {"incr", Jim_IncrCoreCommand}, + {"while", Jim_WhileCoreCommand}, + {"loop", Jim_LoopCoreCommand}, + {"for", Jim_ForCoreCommand}, + {"foreach", Jim_ForeachCoreCommand}, + {"lmap", Jim_LmapCoreCommand}, + {"if", Jim_IfCoreCommand}, + {"switch", Jim_SwitchCoreCommand}, + {"list", Jim_ListCoreCommand}, + {"lindex", Jim_LindexCoreCommand}, + {"lset", Jim_LsetCoreCommand}, + {"lsearch", Jim_LsearchCoreCommand}, + {"llength", Jim_LlengthCoreCommand}, + {"lappend", Jim_LappendCoreCommand}, + {"linsert", Jim_LinsertCoreCommand}, + {"lreplace", Jim_LreplaceCoreCommand}, + {"lsort", Jim_LsortCoreCommand}, + {"append", Jim_AppendCoreCommand}, + {"debug", Jim_DebugCoreCommand}, + {"eval", Jim_EvalCoreCommand}, + {"uplevel", Jim_UplevelCoreCommand}, + {"expr", Jim_ExprCoreCommand}, + {"break", Jim_BreakCoreCommand}, + {"continue", Jim_ContinueCoreCommand}, + {"proc", Jim_ProcCoreCommand}, + {"concat", Jim_ConcatCoreCommand}, + {"return", Jim_ReturnCoreCommand}, + {"upvar", Jim_UpvarCoreCommand}, + {"global", Jim_GlobalCoreCommand}, + {"string", Jim_StringCoreCommand}, + {"time", Jim_TimeCoreCommand}, + {"exit", Jim_ExitCoreCommand}, + {"catch", Jim_CatchCoreCommand}, +#ifdef JIM_REFERENCES + {"ref", Jim_RefCoreCommand}, + {"getref", Jim_GetrefCoreCommand}, + {"setref", Jim_SetrefCoreCommand}, + {"finalize", Jim_FinalizeCoreCommand}, + {"collect", Jim_CollectCoreCommand}, +#endif + {"rename", Jim_RenameCoreCommand}, + {"dict", Jim_DictCoreCommand}, + {"subst", Jim_SubstCoreCommand}, + {"info", Jim_InfoCoreCommand}, + {"exists", Jim_ExistsCoreCommand}, + {"split", Jim_SplitCoreCommand}, + {"join", Jim_JoinCoreCommand}, + {"format", Jim_FormatCoreCommand}, + {"scan", Jim_ScanCoreCommand}, + {"error", Jim_ErrorCoreCommand}, + {"lrange", Jim_LrangeCoreCommand}, + {"lrepeat", Jim_LrepeatCoreCommand}, + {"env", Jim_EnvCoreCommand}, + {"source", Jim_SourceCoreCommand}, + {"lreverse", Jim_LreverseCoreCommand}, + {"range", Jim_RangeCoreCommand}, + {"rand", Jim_RandCoreCommand}, + {"tailcall", Jim_TailcallCoreCommand}, + {"local", Jim_LocalCoreCommand}, + {"upcall", Jim_UpcallCoreCommand}, + {NULL, NULL}, +}; + +void Jim_RegisterCoreCommands(Jim_Interp *interp) +{ + int i = 0; + + while (Jim_CoreCommandsTable[i].name != NULL) { + Jim_CreateCommand(interp, + Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL); + i++; + } +} + +/* ----------------------------------------------------------------------------- + * Interactive prompt + * ---------------------------------------------------------------------------*/ +void Jim_MakeErrorMessage(Jim_Interp *interp) +{ + Jim_Obj *argv[2]; + + argv[0] = Jim_NewStringObj(interp, "errorInfo", -1); + argv[1] = interp->result; + + Jim_EvalObjVector(interp, 2, argv); +} + +static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype, + const char *prefix, const char *const *tablePtr, const char *name) +{ + int count; + char **tablePtrSorted; + int i; + + for (count = 0; tablePtr[count]; count++) { + } + + if (name == NULL) { + name = "option"; + } + + Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg); + tablePtrSorted = Jim_Alloc(sizeof(char *) * count); + memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count); + qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers); + for (i = 0; i < count; i++) { + if (i + 1 == count && count > 1) { + Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1); + } + Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL); + if (i + 1 != count) { + Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1); + } + } + Jim_Free(tablePtrSorted); +} + +int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr, + const char *const *tablePtr, int *indexPtr, const char *name, int flags) +{ + const char *bad = "bad "; + const char *const *entryPtr = NULL; + int i; + int match = -1; + int arglen; + const char *arg = Jim_GetString(objPtr, &arglen); + + *indexPtr = -1; + + for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) { + if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) { + /* Found an exact match */ + *indexPtr = i; + return JIM_OK; + } + if (flags & JIM_ENUM_ABBREV) { + /* Accept an unambiguous abbreviation. + * Note that '-' doesnt' consitute a valid abbreviation + */ + if (strncmp(arg, *entryPtr, arglen) == 0) { + if (*arg == '-' && arglen == 1) { + break; + } + if (match >= 0) { + bad = "ambiguous "; + goto ambiguous; + } + match = i; + } + } + } + + /* If we had an unambiguous partial match */ + if (match >= 0) { + *indexPtr = match; + return JIM_OK; + } + + ambiguous: + if (flags & JIM_ERRMSG) { + JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name); + } + return JIM_ERR; +} + +int Jim_FindByName(const char *name, const char * const array[], size_t len) +{ + int i; + + for (i = 0; i < (int)len; i++) { + if (array[i] && strcmp(array[i], name) == 0) { + return i; + } + } + return -1; +} + +int Jim_IsDict(Jim_Obj *objPtr) +{ + return objPtr->typePtr == &dictObjType; +} + +int Jim_IsList(Jim_Obj *objPtr) +{ + return objPtr->typePtr == &listObjType; +} + +/** + * Very simple printf-like formatting, designed for error messages. + * + * The format may contain up to 5 '%s' or '%#s', corresponding to variable arguments. + * The resulting string is created and set as the result. + * + * Each '%s' should correspond to a regular string parameter. + * Each '%#s' should correspond to a (Jim_Obj *) parameter. + * Any other printf specifier is not allowed (but %% is allowed for the % character). + * + * e.g. Jim_SetResultFormatted(interp, "Bad option \"%#s\" in proc \"%#s\"", optionObjPtr, procNamePtr); + * + * Note: We take advantage of the fact that printf has the same behaviour for both %s and %#s + */ +void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...) +{ + /* Initial space needed */ + int len = strlen(format); + int extra = 0; + int n = 0; + const char *params[5]; + char *buf; + va_list args; + int i; + + va_start(args, format); + + for (i = 0; i < len && n < 5; i++) { + int l; + + if (strncmp(format + i, "%s", 2) == 0) { + params[n] = va_arg(args, char *); + + l = strlen(params[n]); + } + else if (strncmp(format + i, "%#s", 3) == 0) { + Jim_Obj *objPtr = va_arg(args, Jim_Obj *); + + params[n] = Jim_GetString(objPtr, &l); + } + else { + if (format[i] == '%') { + i++; + } + continue; + } + n++; + extra += l; + } + + len += extra; + buf = Jim_Alloc(len + 1); + len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]); + + Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len)); +} + +/* stubs */ +#ifndef jim_ext_package +int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags) +{ + return JIM_OK; +} +#endif +#ifndef jim_ext_aio +FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj) +{ + Jim_SetResultString(interp, "aio not enabled", -1); + return NULL; +} +#endif + + +/* + * Local Variables: *** + * c-basic-offset: 4 *** + * tab-width: 4 *** + * End: *** + */ diff --git a/release/src/router/usbmodeswitch/jim/jim.h b/release/src/router/usbmodeswitch/jim/jim.h new file mode 100644 index 0000000000..fdc9db98ef --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jim.h @@ -0,0 +1,917 @@ +/* Jim - A small embeddable Tcl interpreter + * + * Copyright 2005 Salvatore Sanfilippo + * Copyright 2005 Clemens Hintze + * Copyright 2005 patthoyts - Pat Thoyts + * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com + * Copyright 2008 Andrew Lunn + * Copyright 2008 Duane Ellis + * Copyright 2008 Uwe Klein + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + * + *--- Inline Header File Documentation --- + * [By Duane Ellis, openocd@duaneellis.com, 8/18/8] + * + * Belief is "Jim" would greatly benifit if Jim Internals where + * documented in some way - form whatever, and perhaps - the package: + * 'doxygen' is the correct approach to do that. + * + * Details, see: http://www.stack.nl/~dimitri/doxygen/ + * + * To that end please follow these guide lines: + * + * (A) Document the PUBLIC api in the .H file. + * + * (B) Document JIM Internals, in the .C file. + * + * (C) Remember JIM is embedded in other packages, to that end do + * not assume that your way of documenting is the right way, Jim's + * public documentation should be agnostic, such that it is some + * what agreeable with the "package" that is embedding JIM inside + * of it's own doxygen documentation. + * + * (D) Use minimal Doxygen tags. + * + * This will be an "ongoing work in progress" for some time. + **/ + +#ifndef __JIM__H +#define __JIM__H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include /* for the FILE typedef definition */ +#include /* In order to export the Jim_Free() macro */ +#include /* In order to get type va_list */ + +/* ----------------------------------------------------------------------------- + * System configuration + * autoconf (configure) will set these + * ---------------------------------------------------------------------------*/ +#include + +#ifndef HAVE_NO_AUTOCONF +#include +#endif + +/* ----------------------------------------------------------------------------- + * Compiler specific fixes. + * ---------------------------------------------------------------------------*/ + +/* Long Long type and related issues */ +#ifndef jim_wide +# ifdef HAVE_LONG_LONG +# define jim_wide long long +# ifndef LLONG_MAX +# define LLONG_MAX 9223372036854775807LL +# endif +# ifndef LLONG_MIN +# define LLONG_MIN (-LLONG_MAX - 1LL) +# endif +# define JIM_WIDE_MIN LLONG_MIN +# define JIM_WIDE_MAX LLONG_MAX +# else +# define jim_wide long +# define JIM_WIDE_MIN LONG_MIN +# define JIM_WIDE_MAX LONG_MAX +# endif + +/* ----------------------------------------------------------------------------- + * LIBC specific fixes + * ---------------------------------------------------------------------------*/ + +# ifdef HAVE_LONG_LONG +# define JIM_WIDE_MODIFIER "lld" +# else +# define JIM_WIDE_MODIFIER "ld" +# define strtoull strtoul +# endif +#endif + +#define UCHAR(c) ((unsigned char)(c)) + +/* ----------------------------------------------------------------------------- + * Exported defines + * ---------------------------------------------------------------------------*/ + +/* Jim version numbering: every version of jim is marked with a + * successive integer number. This is version 0. The first + * stable version will be 1, then 2, 3, and so on. */ +#define JIM_VERSION 72 + +#define JIM_OK 0 +#define JIM_ERR 1 +#define JIM_RETURN 2 +#define JIM_BREAK 3 +#define JIM_CONTINUE 4 +#define JIM_SIGNAL 5 +#define JIM_EXIT 6 +/* The following are internal codes and should never been seen/used */ +#define JIM_EVAL 7 + +#define JIM_MAX_NESTING_DEPTH 1000 /* default max nesting depth */ + +/* Some function get an integer argument with flags to change + * the behaviour. */ +#define JIM_NONE 0 /* no flags set */ +#define JIM_ERRMSG 1 /* set an error message in the interpreter. */ + +#define JIM_UNSHARED 4 /* Flag to Jim_GetVariable() */ + +/* Flags for Jim_SubstObj() */ +#define JIM_SUBST_NOVAR 1 /* don't perform variables substitutions */ +#define JIM_SUBST_NOCMD 2 /* don't perform command substitutions */ +#define JIM_SUBST_NOESC 4 /* don't perform escapes substitutions */ +#define JIM_SUBST_FLAG 128 /* flag to indicate that this is a real substition object */ + +/* Unused arguments generate annoying warnings... */ +#define JIM_NOTUSED(V) ((void) V) + +/* Flags for Jim_GetEnum() */ +#define JIM_ENUM_ABBREV 2 /* Allow unambiguous abbreviation */ + +/* Flags used by API calls getting a 'nocase' argument. */ +#define JIM_CASESENS 0 /* case sensitive */ +#define JIM_NOCASE 1 /* no case */ + +/* Filesystem related */ +#define JIM_PATH_LEN 1024 + +/* Newline, some embedded system may need -DJIM_CRLF */ +#ifdef JIM_CRLF +#define JIM_NL "\r\n" +#else +#define JIM_NL "\n" +#endif + +#define JIM_LIBPATH "auto_path" +#define JIM_INTERACTIVE "tcl_interactive" + +/* ----------------------------------------------------------------------------- + * Stack + * ---------------------------------------------------------------------------*/ + +typedef struct Jim_Stack { + int len; + int maxlen; + void **vector; +} Jim_Stack; + +/* ----------------------------------------------------------------------------- + * Hash table + * ---------------------------------------------------------------------------*/ + +typedef struct Jim_HashEntry { + const void *key; + union { + void *val; + int intval; + } u; + struct Jim_HashEntry *next; +} Jim_HashEntry; + +typedef struct Jim_HashTableType { + unsigned int (*hashFunction)(const void *key); + const void *(*keyDup)(void *privdata, const void *key); + void *(*valDup)(void *privdata, const void *obj); + int (*keyCompare)(void *privdata, const void *key1, const void *key2); + void (*keyDestructor)(void *privdata, const void *key); + void (*valDestructor)(void *privdata, void *obj); +} Jim_HashTableType; + +typedef struct Jim_HashTable { + Jim_HashEntry **table; + const Jim_HashTableType *type; + unsigned int size; + unsigned int sizemask; + unsigned int used; + unsigned int collisions; + void *privdata; +} Jim_HashTable; + +typedef struct Jim_HashTableIterator { + Jim_HashTable *ht; + int index; + Jim_HashEntry *entry, *nextEntry; +} Jim_HashTableIterator; + +/* This is the initial size of every hash table */ +#define JIM_HT_INITIAL_SIZE 16 + +/* ------------------------------- Macros ------------------------------------*/ +#define Jim_FreeEntryVal(ht, entry) \ + if ((ht)->type->valDestructor) \ + (ht)->type->valDestructor((ht)->privdata, (entry)->u.val) + +#define Jim_SetHashVal(ht, entry, _val_) do { \ + if ((ht)->type->valDup) \ + entry->u.val = (ht)->type->valDup((ht)->privdata, _val_); \ + else \ + entry->u.val = (_val_); \ +} while(0) + +#define Jim_FreeEntryKey(ht, entry) \ + if ((ht)->type->keyDestructor) \ + (ht)->type->keyDestructor((ht)->privdata, (entry)->key) + +#define Jim_SetHashKey(ht, entry, _key_) do { \ + if ((ht)->type->keyDup) \ + entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \ + else \ + entry->key = (_key_); \ +} while(0) + +#define Jim_CompareHashKeys(ht, key1, key2) \ + (((ht)->type->keyCompare) ? \ + (ht)->type->keyCompare((ht)->privdata, key1, key2) : \ + (key1) == (key2)) + +#define Jim_HashKey(ht, key) (ht)->type->hashFunction(key) + +#define Jim_GetHashEntryKey(he) ((he)->key) +#define Jim_GetHashEntryVal(he) ((he)->val) +#define Jim_GetHashTableCollisions(ht) ((ht)->collisions) +#define Jim_GetHashTableSize(ht) ((ht)->size) +#define Jim_GetHashTableUsed(ht) ((ht)->used) + +/* ----------------------------------------------------------------------------- + * Jim_Obj structure + * ---------------------------------------------------------------------------*/ + +/* ----------------------------------------------------------------------------- + * Jim object. This is mostly the same as Tcl_Obj itself, + * with the addition of the 'prev' and 'next' pointers. + * In Jim all the objects are stored into a linked list for GC purposes, + * so that it's possible to access every object living in a given interpreter + * sequentially. When an object is freed, it's moved into a different + * linked list, used as object pool. + * + * The refcount of a freed object is always -1. + * ---------------------------------------------------------------------------*/ +typedef struct Jim_Obj { + int refCount; /* reference count */ + char *bytes; /* string representation buffer. NULL = no string repr. */ + int length; /* number of bytes in 'bytes', not including the numterm. */ + const struct Jim_ObjType *typePtr; /* object type. */ + /* Internal representation union */ + union { + /* integer number type */ + jim_wide wideValue; + /* hashed object type value */ + int hashValue; + /* index type */ + int indexValue; + /* return code type */ + int returnCode; + /* double number type */ + double doubleValue; + /* Generic pointer */ + void *ptr; + /* Generic two pointers value */ + struct { + void *ptr1; + void *ptr2; + } twoPtrValue; + /* Variable object */ + struct { + unsigned jim_wide callFrameId; + struct Jim_Var *varPtr; + } varValue; + /* Command object */ + struct { + unsigned jim_wide procEpoch; + struct Jim_Cmd *cmdPtr; + } cmdValue; + /* List object */ + struct { + struct Jim_Obj **ele; /* Elements vector */ + int len; /* Length */ + int maxLen; /* Allocated 'ele' length */ + } listValue; + /* String type */ + struct { + int maxLength; + int charLength; /* utf-8 char length. -1 if unknown */ + } strValue; + /* Reference type */ + struct { + jim_wide id; + struct Jim_Reference *refPtr; + } refValue; + /* Source type */ + struct { + struct Jim_Obj *fileNameObj; + int lineNumber; + } sourceValue; + /* Dict substitution type */ + struct { + struct Jim_Obj *varNameObjPtr; + struct Jim_Obj *indexObjPtr; + } dictSubstValue; + /* tagged binary type */ + struct { + unsigned char *data; + size_t len; + } binaryValue; + /* Regular expression pattern */ + struct { + unsigned flags; + void *compre; /* really an allocated (regex_t *) */ + } regexpValue; + struct { + int line; + int argc; + } scriptLineValue; + } internalRep; + /* This are 8 or 16 bytes more for every object + * but this is required for efficient garbage collection + * of Jim references. */ + struct Jim_Obj *prevObjPtr; /* pointer to the prev object. */ + struct Jim_Obj *nextObjPtr; /* pointer to the next object. */ +} Jim_Obj; + +/* Jim_Obj related macros */ +#define Jim_IncrRefCount(objPtr) \ + ++(objPtr)->refCount +#define Jim_DecrRefCount(interp, objPtr) \ + if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr) +#define Jim_IsShared(objPtr) \ + ((objPtr)->refCount > 1) + +/* This macro is used when we allocate a new object using + * Jim_New...Obj(), but for some error we need to destroy it. + * Instead to use Jim_IncrRefCount() + Jim_DecrRefCount() we + * can just call Jim_FreeNewObj. To call Jim_Free directly + * seems too raw, the object handling may change and we want + * that Jim_FreeNewObj() can be called only against objects + * that are belived to have refcount == 0. */ +#define Jim_FreeNewObj Jim_FreeObj + +/* Free the internal representation of the object. */ +#define Jim_FreeIntRep(i,o) \ + if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \ + (o)->typePtr->freeIntRepProc(i, o) + +/* Get the internal representation pointer */ +#define Jim_GetIntRepPtr(o) (o)->internalRep.ptr + +/* Set the internal representation pointer */ +#define Jim_SetIntRepPtr(o, p) \ + (o)->internalRep.ptr = (p) + +/* The object type structure. + * There are four methods. + * + * - FreeIntRep is used to free the internal representation of the object. + * Can be NULL if there is nothing to free. + * - DupIntRep is used to duplicate the internal representation of the object. + * If NULL, when an object is duplicated, the internalRep union is + * directly copied from an object to another. + * Note that it's up to the caller to free the old internal repr of the + * object before to call the Dup method. + * - UpdateString is used to create the string from the internal repr. + * - setFromAny is used to convert the current object into one of this type. + */ + +struct Jim_Interp; + +typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp, + struct Jim_Obj *objPtr); +typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp, + struct Jim_Obj *srcPtr, Jim_Obj *dupPtr); +typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr); + +typedef struct Jim_ObjType { + const char *name; /* The name of the type. */ + Jim_FreeInternalRepProc *freeIntRepProc; + Jim_DupInternalRepProc *dupIntRepProc; + Jim_UpdateStringProc *updateStringProc; + int flags; +} Jim_ObjType; + +/* Jim_ObjType flags */ +#define JIM_TYPE_NONE 0 /* No flags */ +#define JIM_TYPE_REFERENCES 1 /* The object may contain referneces. */ + +/* Starting from 1 << 20 flags are reserved for private uses of + * different calls. This way the same 'flags' argument may be used + * to pass both global flags and private flags. */ +#define JIM_PRIV_FLAG_SHIFT 20 + +/* ----------------------------------------------------------------------------- + * Call frame, vars, commands structures + * ---------------------------------------------------------------------------*/ + +/* Call frame */ +typedef struct Jim_CallFrame { + unsigned jim_wide id; /* Call Frame ID. Used for caching. */ + int level; /* Level of this call frame. 0 = global */ + struct Jim_HashTable vars; /* Where local vars are stored */ + struct Jim_HashTable *staticVars; /* pointer to procedure static vars */ + struct Jim_CallFrame *parentCallFrame; + Jim_Obj *const *argv; /* object vector of the current procedure call. */ + int argc; /* number of args of the current procedure call. */ + Jim_Obj *procArgsObjPtr; /* arglist object of the running procedure */ + Jim_Obj *procBodyObjPtr; /* body object of the running procedure */ + struct Jim_CallFrame *nextFramePtr; + Jim_Obj *fileNameObj; /* file and line of caller of this proc (if available) */ + int line; +} Jim_CallFrame; + +/* The var structure. It just holds the pointer of the referenced + * object. If linkFramePtr is not NULL the variable is a link + * to a variable of name store on objPtr living on the given callframe + * (this happens when the [global] or [upvar] command is used). + * The interp in order to always know how to free the Jim_Obj associated + * with a given variable because In Jim objects memory managment is + * bound to interpreters. */ +typedef struct Jim_Var { + Jim_Obj *objPtr; + struct Jim_CallFrame *linkFramePtr; +} Jim_Var; + +/* The cmd structure. */ +typedef int (*Jim_CmdProc)(struct Jim_Interp *interp, int argc, + Jim_Obj *const *argv); +typedef void (*Jim_DelCmdProc)(struct Jim_Interp *interp, void *privData); + + + +/* A command is implemented in C if funcPtr is != NULL, otherwise + * it's a Tcl procedure with the arglist and body represented by the + * two objects referenced by arglistObjPtr and bodyoObjPtr. */ +typedef struct Jim_Cmd { + int inUse; /* Reference count */ + int isproc; /* Is this a procedure? */ + union { + struct { + /* native (C) command */ + Jim_CmdProc cmdProc; /* The command implementation */ + Jim_DelCmdProc delProc; /* Called when the command is deleted if != NULL */ + void *privData; /* command-private data available via Jim_CmdPrivData() */ + } native; + struct { + /* Tcl procedure */ + Jim_Obj *argListObjPtr; + Jim_Obj *bodyObjPtr; + Jim_HashTable *staticVars; /* Static vars hash table. NULL if no statics. */ + struct Jim_Cmd *prevCmd; /* Previous command defn if proc created 'local' */ + int argListLen; /* Length of argListObjPtr */ + int reqArity; /* Number of required parameters */ + int optArity; /* Number of optional parameters */ + int argsPos; /* Position of 'args', if specified, or -1 */ + int upcall; /* True if proc is currently in upcall */ + struct Jim_ProcArg { + Jim_Obj *nameObjPtr; /* Name of this arg */ + Jim_Obj *defaultObjPtr; /* Default value, (or rename for $args) */ + } *arglist; + } proc; + } u; +} Jim_Cmd; + +/* Pseudo Random Number Generator State structure */ +typedef struct Jim_PrngState { + unsigned char sbox[256]; + unsigned int i, j; +} Jim_PrngState; + +/* ----------------------------------------------------------------------------- + * Jim interpreter structure. + * Fields similar to the real Tcl interpreter structure have the same names. + * ---------------------------------------------------------------------------*/ +typedef struct Jim_Interp { + Jim_Obj *result; /* object returned by the last command called. */ + int errorLine; /* Error line where an error occurred. */ + Jim_Obj *errorFileNameObj; /* Error file where an error occurred. */ + int addStackTrace; /* > 0 If a level should be added to the stack trace */ + int maxNestingDepth; /* Used for infinite loop detection. */ + int returnCode; /* Completion code to return on JIM_RETURN. */ + int returnLevel; /* Current level of 'return -level' */ + int exitCode; /* Code to return to the OS on JIM_EXIT. */ + long id; /* Hold unique id for various purposes */ + int signal_level; /* A nesting level of catch -signal */ + jim_wide sigmask; /* Bit mask of caught signals, or 0 if none */ + int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask); /* Set a result for the sigmask */ + Jim_CallFrame *framePtr; /* Pointer to the current call frame */ + Jim_CallFrame *topFramePtr; /* toplevel/global frame pointer. */ + struct Jim_HashTable commands; /* Commands hash table */ + unsigned jim_wide procEpoch; /* Incremented every time the result + of procedures names lookup caching + may no longer be valid. */ + unsigned jim_wide callFrameEpoch; /* Incremented every time a new + callframe is created. This id is used for the + 'ID' field contained in the Jim_CallFrame + structure. */ + int local; /* If 'local' is in effect, newly defined procs keep a reference to the old defn */ + Jim_Obj *liveList; /* Linked list of all the live objects. */ + Jim_Obj *freeList; /* Linked list of all the unused objects. */ + Jim_Obj *currentScriptObj; /* Script currently in execution. */ + Jim_Obj *emptyObj; /* Shared empty string object. */ + Jim_Obj *trueObj; /* Shared true int object. */ + Jim_Obj *falseObj; /* Shared false int object. */ + unsigned jim_wide referenceNextId; /* Next id for reference. */ + struct Jim_HashTable references; /* References hash table. */ + jim_wide lastCollectId; /* reference max Id of the last GC + execution. It's set to -1 while the collection + is running as sentinel to avoid to recursive + calls via the [collect] command inside + finalizers. */ + time_t lastCollectTime; /* unix time of the last GC execution */ + Jim_Obj *stackTrace; /* Stack trace object. */ + Jim_Obj *errorProc; /* Name of last procedure which returned an error */ + Jim_Obj *unknown; /* Unknown command cache */ + int unknown_called; /* The unknown command has been invoked */ + int errorFlag; /* Set if an error occurred during execution. */ + void *cmdPrivData; /* Used to pass the private data pointer to + a command. It is set to what the user specified + via Jim_CreateCommand(). */ + + struct Jim_CallFrame *freeFramesList; /* list of CallFrame structures. */ + struct Jim_HashTable assocData; /* per-interp storage for use by packages */ + Jim_PrngState *prngState; /* per interpreter Random Number Gen. state. */ + struct Jim_HashTable packages; /* Provided packages hash table */ + Jim_Stack *localProcs; /* procs to be destroyed on end of evaluation */ + Jim_Stack *loadHandles; /* handles of loaded modules [load] */ +} Jim_Interp; + +/* Currently provided as macro that performs the increment. + * At some point may be a real function doing more work. + * The proc epoch is used in order to know when a command lookup + * cached can no longer considered valid. */ +#define Jim_InterpIncrProcEpoch(i) (i)->procEpoch++ +#define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l)) +#define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval)) +/* Note: Using trueObj and falseObj here makes some things slower...*/ +#define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b) +#define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj) +#define Jim_GetResult(i) ((i)->result) +#define Jim_CmdPrivData(i) ((i)->cmdPrivData) +#define Jim_String(o) Jim_GetString((o), NULL) + +/* Note that 'o' is expanded only one time inside this macro, + * so it's safe to use side effects. */ +#define Jim_SetResult(i,o) do { \ + Jim_Obj *_resultObjPtr_ = (o); \ + Jim_IncrRefCount(_resultObjPtr_); \ + Jim_DecrRefCount(i,(i)->result); \ + (i)->result = _resultObjPtr_; \ +} while(0) + +/* Use this for filehandles, etc. which need a unique id */ +#define Jim_GetId(i) (++(i)->id) + +/* Reference structure. The interpreter pointer is held within privdata member in HashTable */ +#define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference + string representation must be fixed length. */ +typedef struct Jim_Reference { + Jim_Obj *objPtr; + Jim_Obj *finalizerCmdNamePtr; + char tag[JIM_REFERENCE_TAGLEN+1]; +} Jim_Reference; + +/* ----------------------------------------------------------------------------- + * Exported API prototypes. + * ---------------------------------------------------------------------------*/ + +/* Macros that are common for extensions and core. */ +#define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0) + +/* The core includes real prototypes, extensions instead + * include a global function pointer for every function exported. + * Once the extension calls Jim_InitExtension(), the global + * functon pointers are set to the value of the STUB table + * contained in the Jim_Interp structure. + * + * This makes Jim able to load extensions even if it is statically + * linked itself, and to load extensions compiled with different + * versions of Jim (as long as the API is still compatible.) */ + +/* Macros are common for core and extensions */ +#define Jim_FreeHashTableIterator(iter) Jim_Free(iter) + +#define JIM_EXPORT + +/* Memory allocation */ +JIM_EXPORT void *Jim_Alloc (int size); +JIM_EXPORT void *Jim_Realloc(void *ptr, int size); +JIM_EXPORT void Jim_Free (void *ptr); +JIM_EXPORT char * Jim_StrDup (const char *s); +JIM_EXPORT char *Jim_StrDupLen(const char *s, int l); + +/* environment */ +JIM_EXPORT char **Jim_GetEnviron(void); +JIM_EXPORT void Jim_SetEnviron(char **env); + +/* evaluation */ +JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script); +/* in C code, you can do this and get better error messages */ +/* Jim_EvalSource( interp, __FILE__, __LINE__ , "some tcl commands"); */ +JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script); +/* Backwards compatibility */ +#define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S)) + +JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script); +JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename); +JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename); +JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr); +JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc, + Jim_Obj *const *objv); +JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, + int objc, Jim_Obj *const *objv); +#define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov)) +JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr, + Jim_Obj **resObjPtrPtr, int flags); + +/* stack */ +JIM_EXPORT void Jim_InitStack(Jim_Stack *stack); +JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack); +JIM_EXPORT int Jim_StackLen(Jim_Stack *stack); +JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element); +JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack); +JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack); +JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr)); + +/* hash table */ +JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht, + const Jim_HashTableType *type, void *privdata); +JIM_EXPORT int Jim_ExpandHashTable (Jim_HashTable *ht, + unsigned int size); +JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key, + void *val); +JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht, + const void *key, void *val); +JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht, + const void *key); +JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht); +JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht, + const void *key); +JIM_EXPORT int Jim_ResizeHashTable (Jim_HashTable *ht); +JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator + (Jim_HashTable *ht); +JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry + (Jim_HashTableIterator *iter); + +/* objects */ +JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp); +JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr); +JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr); +JIM_EXPORT void Jim_InitStringRep (Jim_Obj *objPtr, const char *bytes, + int length); +JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp, + Jim_Obj *objPtr); +JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr, + int *lenPtr); +JIM_EXPORT int Jim_Length(Jim_Obj *objPtr); + +/* string object */ +JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp, + const char *s, int len); +JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, + const char *s, int charlen); +JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp, + char *s, int len); +JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr, + const char *str, int len); +JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr, + Jim_Obj *appendObjPtr); +JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp, + Jim_Obj *objPtr, ...); +JIM_EXPORT int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr); +JIM_EXPORT int Jim_StringMatchObj (Jim_Interp *interp, Jim_Obj *patternObjPtr, + Jim_Obj *objPtr, int nocase); +JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp, + Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, + Jim_Obj *lastObjPtr); +JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp, + Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv); +JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr, + Jim_Obj *fmtObjPtr, int flags); +JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp, + Jim_Obj *objPtr, const char *str); +JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, + Jim_Obj *secondObjPtr, int nocase); +JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr); + +/* reference object */ +JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp, + Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr); +JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp, + Jim_Obj *objPtr); +JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr); +JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr); + +/* interpreter */ +JIM_EXPORT Jim_Interp * Jim_CreateInterp (void); +JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i); +JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp); +JIM_EXPORT const char *Jim_ReturnCode(int code); +JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...); + +/* commands */ +JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp); +JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp, + const char *cmdName, Jim_CmdProc cmdProc, void *privData, + Jim_DelCmdProc delProc); +JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp, + const char *cmdName); +JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp, + const char *oldName, const char *newName); +JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp, + Jim_Obj *objPtr, int flags); +JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp, + Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr); +JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp, + const char *name, Jim_Obj *objPtr); +JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp, + const char *name, Jim_Obj *objPtr); +JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp, + const char *name, const char *val); +JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp, + Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr, + Jim_CallFrame *targetCallFrame); +JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp, + Jim_Obj *nameObjPtr, int flags); +JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp, + Jim_Obj *nameObjPtr, int flags); +JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp, + const char *name, int flags); +JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp, + const char *name, int flags); +JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp, + Jim_Obj *nameObjPtr, int flags); + +/* call frame */ +JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, + Jim_Obj *levelObjPtr); + +/* garbage collection */ +JIM_EXPORT int Jim_Collect (Jim_Interp *interp); +JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp); + +/* index object */ +JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr, + int *indexPtr); + +/* list object */ +JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp, + Jim_Obj *const *elements, int len); +JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp, + Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec); +JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp, + Jim_Obj *listPtr, Jim_Obj *objPtr); +JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp, + Jim_Obj *listPtr, Jim_Obj *appendListPtr); +JIM_EXPORT int Jim_ListLength (Jim_Interp *interp, Jim_Obj *objPtr); +JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt, + int listindex, Jim_Obj **objPtrPtr, int seterr); +JIM_EXPORT int Jim_SetListIndex (Jim_Interp *interp, + Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc, + Jim_Obj *newObjPtr); +JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc, + Jim_Obj *const *objv); + +/* dict object */ +JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp, + Jim_Obj *const *elements, int len); +JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr, + Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags); +JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp, + Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc, + Jim_Obj **objPtrPtr, int flags); +JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp, + Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc, + Jim_Obj *newObjPtr, int flags); +JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp, + Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len); +JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, + Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr); +JIM_EXPORT int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj); +JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr); + +/* return code object */ +JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr, + int *intPtr); + +/* expression object */ +JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp, + Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr); +JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp, + Jim_Obj *exprObjPtr, int *boolPtr); + +/* integer object */ +JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr, + jim_wide *widePtr); +JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr, + long *longPtr); +#define Jim_NewWideObj Jim_NewIntObj +JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp, + jim_wide wideValue); + +/* double object */ +JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, + double *doublePtr); +JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, + double doubleValue); +JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue); + +/* shared strings */ +JIM_EXPORT const char * Jim_GetSharedString (Jim_Interp *interp, + const char *str); +JIM_EXPORT void Jim_ReleaseSharedString (Jim_Interp *interp, + const char *str); + +/* commands utilities */ +JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc, + Jim_Obj *const *argv, const char *msg); +JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr, + const char * const *tablePtr, int *indexPtr, const char *name, int flags); +JIM_EXPORT int Jim_ScriptIsComplete (const char *s, int len, + char *stateCharPtr); +/** + * Find a matching name in the array of the given length. + * + * NULL entries are ignored. + * + * Returns the matching index if found, or -1 if not. + */ +JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len); + +/* package utilities */ +typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data); +JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key); +JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key, + Jim_InterpDeleteProc *delProc, void *data); +JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key); + +/* Packages C API */ +/* jim-package.c */ +JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp, + const char *name, const char *ver, int flags); +JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp, + const char *name, int flags); + +/* error messages */ +JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp); + +/* interactive mode */ +JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp); + +/* Misc */ +JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp); +JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base); + +/* jim-load.c */ +JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName); +JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp); + +/* jim-aio.c */ +JIM_EXPORT FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command); + + +/* type inspection - avoid where possible */ +JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr); +JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr); + +#ifdef __cplusplus +} +#endif + +#endif /* __JIM__H */ + +/* + * Local Variables: *** + * c-basic-offset: 4 *** + * tab-width: 4 *** + * End: *** + */ diff --git a/release/src/router/usbmodeswitch/jim/jimautoconf.h.in b/release/src/router/usbmodeswitch/jim/jimautoconf.h.in new file mode 100644 index 0000000000..f4eb0e59d9 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jimautoconf.h.in @@ -0,0 +1,145 @@ +/* jimautoconf.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `backtrace' function. */ +#undef HAVE_BACKTRACE + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Have the dlopen function */ +#undef HAVE_DLOPEN + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `getaddrinfo' function. */ +#undef HAVE_GETADDRINFO + +/* Define to 1 if you have the `geteuid' function. */ +#undef HAVE_GETEUID + +/* Define to 1 if you have the `inet_ntop' function. */ +#undef HAVE_INET_NTOP + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if the system has the type `long long'. */ +#undef HAVE_LONG_LONG + +/* Define to 1 if you have the `lstat' function. */ +#undef HAVE_LSTAT + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `mkstemp' function. */ +#undef HAVE_MKSTEMP + +/* Define to 1 if you have the `opendir' function. */ +#undef HAVE_OPENDIR + +/* Define to 1 if you have the `pipe' function. */ +#undef HAVE_PIPE + +/* Have libreadline */ +#undef HAVE_READLINE + +/* Define to 1 if you have the `readlink' function. */ +#undef HAVE_READLINK + +/* Define to 1 if you have the `realpath' function. */ +#undef HAVE_REALPATH + +/* Define to 1 if you have the `regcomp' function. */ +#undef HAVE_REGCOMP + +/* Define to 1 if you have the `sigaction' function. */ +#undef HAVE_SIGACTION + +/* Define to 1 if you have the `sleep' function. */ +#undef HAVE_SLEEP + +/* Have libsqlite */ +#undef HAVE_SQLITE + +/* Have libsqlite3 */ +#undef HAVE_SQLITE3 + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strptime' function. */ +#undef HAVE_STRPTIME + +/* Define to 1 if you have the `sysinfo' function. */ +#undef HAVE_SYSINFO + +/* Define to 1 if you have the `syslog' function. */ +#undef HAVE_SYSLOG + +/* Define to 1 if you have the `sys_siglist' function. */ +#undef HAVE_SYS_SIGLIST + +/* Define to 1 if you have the `sys_signame' function. */ +#undef HAVE_SYS_SIGNAME + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIOS_H + +/* Define to 1 if you have the `ualarm' function. */ +#undef HAVE_UALARM + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `usleep' function. */ +#undef HAVE_USLEEP + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the `waitpid' function. */ +#undef HAVE_WAITPID + +/* No need to declare extern 'environ'. */ +#undef NO_ENVIRON_EXTERN + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS diff --git a/release/src/router/usbmodeswitch/jim/jimregexp.c b/release/src/router/usbmodeswitch/jim/jimregexp.c new file mode 100644 index 0000000000..c652ad4d32 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jimregexp.c @@ -0,0 +1,1756 @@ +/* + * regcomp and regexec -- regsub and regerror are elsewhere + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore, + *** hoptoad!gnu, on 27 Dec 1986, to add \n as an alternative to | + *** to assist in implementing egrep. + *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore, + *** hoptoad!gnu, on 27 Dec 1986, to add \< and \> for word-matching + *** as in BSD grep and ex. + *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore, + *** hoptoad!gnu, on 28 Dec 1986, to optimize characters quoted with \. + *** THIS IS AN ALTERED VERSION. It was altered by James A. Woods, + *** ames!jaw, on 19 June 1987, to quash a regcomp() redundancy. + *** THIS IS AN ALTERED VERSION. It was altered by Christopher Seiwald + *** seiwald@vix.com, on 28 August 1993, for use in jam. Regmagic.h + *** was moved into regexp.h, and the include of regexp.h now uses "'s + *** to avoid conflicting with the system regexp.h. Const, bless its + *** soul, was removed so it can compile everywhere. The declaration + *** of strchr() was in conflict on AIX, so it was removed (as it is + *** happily defined in string.h). + *** THIS IS AN ALTERED VERSION. It was altered by Christopher Seiwald + *** seiwald@perforce.com, on 20 January 2000, to use function prototypes. + *** THIS IS AN ALTERED VERSION. It was altered by Christopher Seiwald + *** seiwald@perforce.com, on 05 November 2002, to const string literals. + * + * THIS IS AN ALTERED VERSION. It was altered by Steve Bennett + * on 16 October 2010, to remove static state and add better Tcl ARE compatibility. + * This includes counted repetitions, UTF-8 support, character classes, + * shorthand character classes, increased number of parentheses to 100, + * backslash escape sequences. It also removes \n as an alternative to |. + * + * Beware that some of this code is subtly aware of the way operator + * precedence is structured in regular expressions. Serious changes in + * regular-expression syntax might require a total rethink. + */ +#include +#include +#include +#include + +#include "jim.h" +#include "jimautoconf.h" +#include "jimregexp.h" +#include "utf8.h" + +#if !defined(HAVE_REGCOMP) || defined(JIM_REGEXP) + +/* + * Structure for regexp "program". This is essentially a linear encoding + * of a nondeterministic finite-state machine (aka syntax charts or + * "railroad normal form" in parsing technology). Each node is an opcode + * plus a "next" pointer, possibly plus an operand. "Next" pointers of + * all nodes except BRANCH implement concatenation; a "next" pointer with + * a BRANCH on both ends of it is connecting two alternatives. (Here we + * have one of the subtle syntax dependencies: an individual BRANCH (as + * opposed to a collection of them) is never concatenated with anything + * because of operator precedence.) The operand of some types of node is + * a literal string; for others, it is a node leading into a sub-FSM. In + * particular, the operand of a BRANCH node is the first node of the branch. + * (NB this is *not* a tree structure: the tail of the branch connects + * to the thing following the set of BRANCHes.) The opcodes are: + */ + +/* This *MUST* be less than (255-20)/2=117 */ +#define REG_MAX_PAREN 100 + +/* definition number opnd? meaning */ +#define END 0 /* no End of program. */ +#define BOL 1 /* no Match "" at beginning of line. */ +#define EOL 2 /* no Match "" at end of line. */ +#define ANY 3 /* no Match any one character. */ +#define ANYOF 4 /* str Match any character in this string. */ +#define ANYBUT 5 /* str Match any character not in this string. */ +#define BRANCH 6 /* node Match this alternative, or the next... */ +#define BACK 7 /* no Match "", "next" ptr points backward. */ +#define EXACTLY 8 /* str Match this string. */ +#define NOTHING 9 /* no Match empty string. */ +#define REP 10 /* max,min Match this (simple) thing [min,max] times. */ +#define REPMIN 11 /* max,min Match this (simple) thing [min,max] times, mininal match. */ +#define REPX 12 /* max,min Match this (complex) thing [min,max] times. */ +#define REPXMIN 13 /* max,min Match this (complex) thing [min,max] times, minimal match. */ + +#define WORDA 15 /* no Match "" at wordchar, where prev is nonword */ +#define WORDZ 16 /* no Match "" at nonwordchar, where prev is word */ +#define OPEN 20 /* no Mark this point in input as start of #n. */ + /* OPEN+1 is number 1, etc. */ +#define CLOSE (OPEN+REG_MAX_PAREN) /* no Analogous to OPEN. */ +#define CLOSE_END (CLOSE+REG_MAX_PAREN) + +/* + * The first byte of the regexp internal "program" is actually this magic + * number; the start node begins in the second byte. + */ +#define REG_MAGIC 0xFADED00D + +/* + * Opcode notes: + * + * BRANCH The set of branches constituting a single choice are hooked + * together with their "next" pointers, since precedence prevents + * anything being concatenated to any individual branch. The + * "next" pointer of the last BRANCH in a choice points to the + * thing following the whole choice. This is also where the + * final "next" pointer of each individual branch points; each + * branch starts with the operand node of a BRANCH node. + * + * BACK Normal "next" pointers all implicitly point forward; BACK + * exists to make loop structures possible. + * + * STAR,PLUS '?', and complex '*' and '+', are implemented as circular + * BRANCH structures using BACK. Simple cases (one character + * per match) are implemented with STAR and PLUS for speed + * and to minimize recursive plunges. + * + * OPEN,CLOSE ...are numbered at compile time. + */ + +/* + * A node is one char of opcode followed by two chars of "next" pointer. + * "Next" pointers are stored as two 8-bit pieces, high order first. The + * value is a positive offset from the opcode of the node containing it. + * An operand, if any, simply follows the node. (Note that much of the + * code generation knows about this implicit relationship.) + * + * Using two bytes for the "next" pointer is vast overkill for most things, + * but allows patterns to get big without disasters. + */ +#define OP(preg, p) (preg->program[p]) +#define NEXT(preg, p) (preg->program[p + 1]) +#define OPERAND(p) ((p) + 2) + +/* + * See regmagic.h for one further detail of program structure. + */ + + +/* + * Utility definitions. + */ + +#define FAIL(R,M) { (R)->err = (M); return (M); } +#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{') +#define META "^$.[()|?{+*" + +/* + * Flags to be passed up and down. + */ +#define HASWIDTH 01 /* Known never to match null string. */ +#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ +#define SPSTART 04 /* Starts with * or +. */ +#define WORST 0 /* Worst case. */ + +#define MAX_REP_COUNT 1000000 + +/* + * Forward declarations for regcomp()'s friends. + */ +static int reg(regex_t *preg, int paren /* Parenthesized? */, int *flagp ); +static int regpiece(regex_t *preg, int *flagp ); +static int regbranch(regex_t *preg, int *flagp ); +static int regatom(regex_t *preg, int *flagp ); +static int regnode(regex_t *preg, int op ); +static int regnext(regex_t *preg, int p ); +static void regc(regex_t *preg, int b ); +static int reginsert(regex_t *preg, int op, int size, int opnd ); +static void regtail_(regex_t *preg, int p, int val, int line ); +static void regoptail(regex_t *preg, int p, int val ); +#define regtail(PREG, P, VAL) regtail_(PREG, P, VAL, __LINE__) + +static int reg_range_find(const int *string, int c); +static const char *str_find(const char *string, int c, int nocase); +static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase); + +/*#define DEBUG*/ +#ifdef DEBUG +int regnarrate = 0; +static void regdump(regex_t *preg); +static const char *regprop( int op ); +#endif + + +/** + * Returns the length of the null-terminated integer sequence. + */ +static int str_int_len(const int *seq) +{ + int n = 0; + while (*seq++) { + n++; + } + return n; +} + +/* + - regcomp - compile a regular expression into internal code + * + * We can't allocate space until we know how big the compiled form will be, + * but we can't compile it (and thus know how big it is) until we've got a + * place to put the code. So we cheat: we compile it twice, once with code + * generation turned off and size counting turned on, and once "for real". + * This also means that we don't allocate space until we are sure that the + * thing really will compile successfully, and we never have to move the + * code and thus invalidate pointers into it. (Note that it has to be in + * one piece because free() must be able to free it all.) + * + * Beware that the optimization-preparation code in here knows about some + * of the structure of the compiled regexp. + */ +int regcomp(regex_t *preg, const char *exp, int cflags) +{ + int scan; + int longest; + unsigned len; + int flags; + +#ifdef DEBUG + fprintf(stderr, "Compiling: '%s'\n", exp); +#endif + memset(preg, 0, sizeof(*preg)); + + if (exp == NULL) + FAIL(preg, REG_ERR_NULL_ARGUMENT); + + /* First pass: determine size, legality. */ + preg->cflags = cflags; + preg->regparse = exp; + /* XXX: For now, start unallocated */ + preg->program = NULL; + preg->proglen = 0; + +#if 1 + /* Allocate space. */ + preg->proglen = (strlen(exp) + 1) * 5; + preg->program = malloc(preg->proglen * sizeof(int)); + if (preg->program == NULL) + FAIL(preg, REG_ERR_NOMEM); +#endif + + /* Note that since we store a magic value as the first item in the program, + * program offsets will never be 0 + */ + regc(preg, REG_MAGIC); + if (reg(preg, 0, &flags) == 0) { + return preg->err; + } + + /* Small enough for pointer-storage convention? */ + if (preg->re_nsub >= REG_MAX_PAREN) /* Probably could be 65535L. */ + FAIL(preg,REG_ERR_TOO_BIG); + + /* Dig out information for optimizations. */ + preg->regstart = 0; /* Worst-case defaults. */ + preg->reganch = 0; + preg->regmust = 0; + preg->regmlen = 0; + scan = 1; /* First BRANCH. */ + if (OP(preg, regnext(preg, scan)) == END) { /* Only one top-level choice. */ + scan = OPERAND(scan); + + /* Starting-point info. */ + if (OP(preg, scan) == EXACTLY) { + preg->regstart = preg->program[OPERAND(scan)]; + } + else if (OP(preg, scan) == BOL) + preg->reganch++; + + /* + * If there's something expensive in the r.e., find the + * longest literal string that must appear and make it the + * regmust. Resolve ties in favor of later strings, since + * the regstart check works with the beginning of the r.e. + * and avoiding duplication strengthens checking. Not a + * strong reason, but sufficient in the absence of others. + */ + if (flags&SPSTART) { + longest = 0; + len = 0; + for (; scan != 0; scan = regnext(preg, scan)) { + if (OP(preg, scan) == EXACTLY) { + int plen = str_int_len(preg->program + OPERAND(scan)); + if (plen >= len) { + longest = OPERAND(scan); + len = plen; + } + } + } + preg->regmust = longest; + preg->regmlen = len; + } + } + +#ifdef DEBUG + regdump(preg); +#endif + + return 0; +} + +/* + - reg - regular expression, i.e. main body or parenthesized thing + * + * Caller must absorb opening parenthesis. + * + * Combining parenthesis handling with the base level of regular expression + * is a trifle forced, but the need to tie the tails of the branches to what + * follows makes it hard to avoid. + */ +static int reg(regex_t *preg, int paren /* Parenthesized? */, int *flagp ) +{ + int ret; + int br; + int ender; + int parno = 0; + int flags; + + *flagp = HASWIDTH; /* Tentatively. */ + + /* Make an OPEN node, if parenthesized. */ + if (paren) { + parno = ++preg->re_nsub; + ret = regnode(preg, OPEN+parno); + } else + ret = 0; + + /* Pick up the branches, linking them together. */ + br = regbranch(preg, &flags); + if (br == 0) + return 0; + if (ret != 0) + regtail(preg, ret, br); /* OPEN -> first. */ + else + ret = br; + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + while (*preg->regparse == '|') { + preg->regparse++; + br = regbranch(preg, &flags); + if (br == 0) + return 0; + regtail(preg, ret, br); /* BRANCH -> BRANCH. */ + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + } + + /* Make a closing node, and hook it on the end. */ + ender = regnode(preg, (paren) ? CLOSE+parno : END); + regtail(preg, ret, ender); + + /* Hook the tails of the branches to the closing node. */ + for (br = ret; br != 0; br = regnext(preg, br)) + regoptail(preg, br, ender); + + /* Check for proper termination. */ + if (paren && *preg->regparse++ != ')') { + preg->err = REG_ERR_UNMATCHED_PAREN; + return 0; + } else if (!paren && *preg->regparse != '\0') { + if (*preg->regparse == ')') { + preg->err = REG_ERR_UNMATCHED_PAREN; + return 0; + } else { + preg->err = REG_ERR_JUNK_ON_END; + return 0; + } + } + + return(ret); +} + +/* + - regbranch - one alternative of an | operator + * + * Implements the concatenation operator. + */ +static int regbranch(regex_t *preg, int *flagp ) +{ + int ret; + int chain; + int latest; + int flags; + + *flagp = WORST; /* Tentatively. */ + + ret = regnode(preg, BRANCH); + chain = 0; + while (*preg->regparse != '\0' && *preg->regparse != ')' && + *preg->regparse != '|') { + latest = regpiece(preg, &flags); + if (latest == 0) + return 0; + *flagp |= flags&HASWIDTH; + if (chain == 0) {/* First piece. */ + *flagp |= flags&SPSTART; + } + else { + regtail(preg, chain, latest); + } + chain = latest; + } + if (chain == 0) /* Loop ran zero times. */ + (void) regnode(preg, NOTHING); + + return(ret); +} + +/* + - regpiece - something followed by possible [*+?] + * + * Note that the branching code sequences used for ? and the general cases + * of * and + are somewhat optimized: they use the same NOTHING node as + * both the endmarker for their branch list and the body of the last branch. + * It might seem that this node could be dispensed with entirely, but the + * endmarker role is not redundant. + */ +static int regpiece(regex_t *preg, int *flagp) +{ + int ret; + char op; + int next; + int flags; + int chain = 0; + int min; + int max; + + ret = regatom(preg, &flags); + if (ret == 0) + return 0; + + op = *preg->regparse; + if (!ISMULT(op)) { + *flagp = flags; + return(ret); + } + + if (!(flags&HASWIDTH) && op != '?') { + preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY; + return 0; + } + + /* Handle braces (counted repetition) by expansion */ + if (op == '{') { + char *end; + + min = strtoul(preg->regparse + 1, &end, 10); + if (end == preg->regparse + 1) { + preg->err = REG_ERR_BAD_COUNT; + return 0; + } + if (*end == '}') { + max = min; + } + else { + preg->regparse = end; + max = strtoul(preg->regparse + 1, &end, 10); + if (*end != '}') { + preg->err = REG_ERR_UNMATCHED_BRACES; + return 0; + } + } + if (end == preg->regparse + 1) { + max = MAX_REP_COUNT; + } + else if (max < min || max >= 100) { + preg->err = REG_ERR_BAD_COUNT; + return 0; + } + if (min >= 100) { + preg->err = REG_ERR_BAD_COUNT; + return 0; + } + + preg->regparse = strchr(preg->regparse, '}'); + } + else { + min = (op == '+'); + max = (op == '?' ? 1 : MAX_REP_COUNT); + } + + if (preg->regparse[1] == '?') { + preg->regparse++; + next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret); + } + else { + next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret); + } + preg->program[ret + 2] = max; + preg->program[ret + 3] = min; + preg->program[ret + 4] = 0; + + *flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART); + + if (!(flags & SIMPLE)) { + int back = regnode(preg, BACK); + regtail(preg, back, ret); + regtail(preg, next, back); + } + + preg->regparse++; + if (ISMULT(*preg->regparse)) { + preg->err = REG_ERR_NESTED_COUNT; + return 0; + } + + return chain ? chain : ret; +} + +/** + * Add all characters in the inclusive range between lower and upper. + * + * Handles a swapped range (upper < lower). + */ +static void reg_addrange(regex_t *preg, int lower, int upper) +{ + if (lower > upper) { + reg_addrange(preg, upper, lower); + } + /* Add a range as length, start */ + regc(preg, upper - lower + 1); + regc(preg, lower); +} + +/** + * Add a null-terminated literal string as a set of ranges. + */ +static void reg_addrange_str(regex_t *preg, const char *str) +{ + while (*str) { + reg_addrange(preg, *str, *str); + str++; + } +} + +/** + * Extracts the next unicode char from utf8. + * + * If 'upper' is set, converts the char to uppercase. + */ +static int reg_utf8_tounicode_case(const char *s, int *uc, int upper) +{ + int l = utf8_tounicode(s, uc); + if (upper) { + *uc = utf8_upper(*uc); + } + return l; +} + +/** + * Converts a hex digit to decimal. + * + * Returns -1 for an invalid hex digit. + */ +static int hexdigitval(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + +/** + * Parses up to 'n' hex digits at 's' and stores the result in *uc. + * + * Returns the number of hex digits parsed. + * If there are no hex digits, returns 0 and stores nothing. + */ +static int parse_hex(const char *s, int n, int *uc) +{ + int val = 0; + int k; + + for (k = 0; k < n; k++) { + int c = hexdigitval(*s++); + if (c == -1) { + break; + } + val = (val << 4) | c; + } + if (k) { + *uc = val; + } + return k; +} + +/** + * Call for chars after a backlash to decode the escape sequence. + * + * Stores the result in *ch. + * + * Returns the number of bytes consumed. + */ +static int reg_decode_escape(const char *s, int *ch) +{ + int n; + const char *s0 = s; + + *ch = *s++; + + switch (*ch) { + case 'b': *ch = '\b'; break; + case 'e': *ch = 27; break; + case 'f': *ch = '\f'; break; + case 'n': *ch = '\n'; break; + case 'r': *ch = '\r'; break; + case 't': *ch = '\t'; break; + case 'v': *ch = '\v'; break; + case 'u': + if ((n = parse_hex(s, 4, ch)) > 0) { + s += n; + } + break; + case 'x': + if ((n = parse_hex(s, 2, ch)) > 0) { + s += n; + } + break; + case '\0': + s--; + *ch = '\\'; + break; + } + return s - s0; +} + +/* + - regatom - the lowest level + * + * Optimization: gobbles an entire sequence of ordinary characters so that + * it can turn them into a single node, which is smaller to store and + * faster to run. Backslashed characters are exceptions, each becoming a + * separate node; the code is simpler that way and it's not worth fixing. + */ +static int regatom(regex_t *preg, int *flagp) +{ + int ret; + int flags; + int nocase = (preg->cflags & REG_ICASE); + + int ch; + int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase); + + *flagp = WORST; /* Tentatively. */ + + preg->regparse += n; + switch (ch) { + /* FIXME: these chars only have meaning at beg/end of pat? */ + case '^': + ret = regnode(preg, BOL); + break; + case '$': + ret = regnode(preg, EOL); + break; + case '.': + ret = regnode(preg, ANY); + *flagp |= HASWIDTH|SIMPLE; + break; + case '[': { + const char *pattern = preg->regparse; + + if (*pattern == '^') { /* Complement of range. */ + ret = regnode(preg, ANYBUT); + pattern++; + } else + ret = regnode(preg, ANYOF); + + /* Special case. If the first char is ']' or '-', it is part of the set */ + if (*pattern == ']' || *pattern == '-') { + reg_addrange(preg, *pattern, *pattern); + pattern++; + } + + while (*pattern && *pattern != ']') { + /* Is this a range? a-z */ + int start; + int end; + + pattern += reg_utf8_tounicode_case(pattern, &start, nocase); + if (start == '\\') { + pattern += reg_decode_escape(pattern, &start); + if (start == 0) { + preg->err = REG_ERR_NULL_CHAR; + return 0; + } + } + if (pattern[0] == '-' && pattern[1]) { + /* skip '-' */ + pattern += utf8_tounicode(pattern, &end); + pattern += reg_utf8_tounicode_case(pattern, &end, nocase); + if (end == '\\') { + pattern += reg_decode_escape(pattern, &end); + if (end == 0) { + preg->err = REG_ERR_NULL_CHAR; + return 0; + } + } + + reg_addrange(preg, start, end); + continue; + } + if (start == '[') { + if (strncmp(pattern, ":alpha:]", 8) == 0) { + if ((preg->cflags & REG_ICASE) == 0) { + reg_addrange(preg, 'a', 'z'); + } + reg_addrange(preg, 'A', 'Z'); + pattern += 8; + continue; + } + if (strncmp(pattern, ":alnum:]", 8) == 0) { + if ((preg->cflags & REG_ICASE) == 0) { + reg_addrange(preg, 'a', 'z'); + } + reg_addrange(preg, 'A', 'Z'); + reg_addrange(preg, '0', '9'); + pattern += 8; + continue; + } + if (strncmp(pattern, ":space:]", 8) == 0) { + reg_addrange_str(preg, " \t\r\n\f\v"); + pattern += 8; + continue; + } + } + /* Not a range, so just add the char */ + reg_addrange(preg, start, start); + } + regc(preg, '\0'); + + if (*pattern) { + pattern++; + } + preg->regparse = pattern; + + *flagp |= HASWIDTH|SIMPLE; + } + break; + case '(': + ret = reg(preg, 1, &flags); + if (ret == 0) + return 0; + *flagp |= flags&(HASWIDTH|SPSTART); + break; + case '\0': + case '|': + case ')': + preg->err = REG_ERR_INTERNAL; + return 0; /* Supposed to be caught earlier. */ + case '?': + case '+': + case '*': + case '{': + preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING; + return 0; + case '\\': + switch (*preg->regparse++) { + case '\0': + preg->err = REG_ERR_TRAILING_BACKSLASH; + return 0; + case '<': + case 'm': + ret = regnode(preg, WORDA); + break; + case '>': + case 'M': + ret = regnode(preg, WORDZ); + break; + case 'd': + ret = regnode(preg, ANYOF); + reg_addrange(preg, '0', '9'); + regc(preg, '\0'); + *flagp |= HASWIDTH|SIMPLE; + break; + case 'w': + ret = regnode(preg, ANYOF); + if ((preg->cflags & REG_ICASE) == 0) { + reg_addrange(preg, 'a', 'z'); + } + reg_addrange(preg, 'A', 'Z'); + reg_addrange(preg, '0', '9'); + reg_addrange(preg, '_', '_'); + regc(preg, '\0'); + *flagp |= HASWIDTH|SIMPLE; + break; + case 's': + ret = regnode(preg, ANYOF); + reg_addrange_str(preg," \t\r\n\f\v"); + regc(preg, '\0'); + *flagp |= HASWIDTH|SIMPLE; + break; + /* FIXME: Someday handle \1, \2, ... */ + default: + /* Handle general quoted chars in exact-match routine */ + /* Back up to include the backslash */ + preg->regparse--; + goto de_fault; + } + break; + de_fault: + default: { + /* + * Encode a string of characters to be matched exactly. + */ + int added = 0; + + /* Back up to pick up the first char of interest */ + preg->regparse -= n; + + ret = regnode(preg, EXACTLY); + + /* Note that a META operator such as ? or * consumes the + * preceding char. + * Thus we must be careful to look ahead by 2 and add the + * last char as it's own EXACTLY if necessary + */ + + /* Until end of string or a META char is reached */ + while (*preg->regparse && strchr(META, *preg->regparse) == NULL) { + n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE)); + if (ch == '\\' && preg->regparse[n]) { + /* Non-trailing backslash. + * Is this a special escape, or a regular escape? + */ + if (strchr("<>mMwds", preg->regparse[n])) { + /* A special escape. All done with EXACTLY */ + break; + } + /* Decode it. Note that we add the length for the escape + * sequence to the length for the backlash so we can skip + * the entire sequence, or not as required. + */ + n += reg_decode_escape(preg->regparse + n, &ch); + if (ch == 0) { + preg->err = REG_ERR_NULL_CHAR; + return 0; + } + } + + /* Now we have one char 'ch' of length 'n'. + * Check to see if the following char is a MULT + */ + + if (ISMULT(preg->regparse[n])) { + /* Yes. But do we already have some EXACTLY chars? */ + if (added) { + /* Yes, so return what we have and pick up the current char next time around */ + break; + } + /* No, so add this single char and finish */ + regc(preg, ch); + added++; + preg->regparse += n; + break; + } + + /* No, so just add this char normally */ + regc(preg, ch); + added++; + preg->regparse += n; + } + regc(preg, '\0'); + + *flagp |= HASWIDTH; + if (added == 1) + *flagp |= SIMPLE; + break; + } + break; + } + + return(ret); +} + +static void reg_grow(regex_t *preg, int n) +{ + if (preg->p + n >= preg->proglen) { + preg->proglen = (preg->p + n) * 2; + preg->program = realloc(preg->program, preg->proglen * sizeof(int)); + } +} + +/* + - regnode - emit a node + */ +/* Location. */ +static int regnode(regex_t *preg, int op) +{ + reg_grow(preg, 2); + + preg->program[preg->p++] = op; + preg->program[preg->p++] = 0; + + /* Return the start of the node */ + return preg->p - 2; +} + +/* + - regc - emit (if appropriate) a byte of code + */ +static void regc(regex_t *preg, int b ) +{ + reg_grow(preg, 1); + preg->program[preg->p++] = b; +} + +/* + - reginsert - insert an operator in front of already-emitted operand + * + * Means relocating the operand. + * Returns the new location of the original operand. + */ +static int reginsert(regex_t *preg, int op, int size, int opnd ) +{ + reg_grow(preg, size); + + /* Move everything from opnd up */ + memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd)); + /* Zero out the new space */ + memset(preg->program + opnd, 0, sizeof(int) * size); + + preg->program[opnd] = op; + + preg->p += size; + + return opnd + size; +} + +/* + - regtail - set the next-pointer at the end of a node chain + */ +static void regtail_(regex_t *preg, int p, int val, int line ) +{ + int scan; + int temp; + int offset; + + /* Find last node. */ + scan = p; + for (;;) { + temp = regnext(preg, scan); + if (temp == 0) + break; + scan = temp; + } + + if (OP(preg, scan) == BACK) + offset = scan - val; + else + offset = val - scan; + + preg->program[scan + 1] = offset; +} + +/* + - regoptail - regtail on operand of first argument; nop if operandless + */ + +static void regoptail(regex_t *preg, int p, int val ) +{ + /* "Operandless" and "op != BRANCH" are synonymous in practice. */ + if (p != 0 && OP(preg, p) == BRANCH) { + regtail(preg, OPERAND(p), val); + } +} + +/* + * regexec and friends + */ + +/* + * Forwards. + */ +static int regtry(regex_t *preg, const char *string ); +static int regmatch(regex_t *preg, int prog); +static int regrepeat(regex_t *preg, int p, int max); + +/* + - regexec - match a regexp against a string + */ +int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags) +{ + const char *s; + int scan; + + /* Be paranoid... */ + if (preg == NULL || preg->program == NULL || string == NULL) { + return REG_ERR_NULL_ARGUMENT; + } + + /* Check validity of program. */ + if (*preg->program != REG_MAGIC) { + return REG_ERR_CORRUPTED; + } + +#ifdef DEBUG + fprintf(stderr, "regexec: %s\n", string); + regdump(preg); +#endif + + preg->eflags = eflags; + preg->pmatch = pmatch; + preg->nmatch = nmatch; + preg->start = string; /* All offsets are computed from here */ + + /* Must clear out the embedded repeat counts */ + for (scan = OPERAND(1); scan != 0; scan = regnext(preg, scan)) { + switch (OP(preg, scan)) { + case REP: + case REPMIN: + case REPX: + case REPXMIN: + preg->program[scan + 4] = 0; + break; + } + } + + /* If there is a "must appear" string, look for it. */ + if (preg->regmust != 0) { + s = string; + while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) { + if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) { + break; + } + s++; + } + if (s == NULL) /* Not present. */ + return REG_NOMATCH; + } + + /* Mark beginning of line for ^ . */ + preg->regbol = string; + + /* Simplest case: anchored match need be tried only once (maybe per line). */ + if (preg->reganch) { + if (eflags & REG_NOTBOL) { + /* This is an anchored search, but not an BOL, so possibly skip to the next line */ + goto nextline; + } + while (1) { + int ret = regtry(preg, string); + if (ret) { + return REG_NOERROR; + } + if (*string) { +nextline: + if (preg->cflags & REG_NEWLINE) { + /* Try the next anchor? */ + string = strchr(string, '\n'); + if (string) { + preg->regbol = ++string; + continue; + } + } + } + return REG_NOMATCH; + } + } + + /* Messy cases: unanchored match. */ + s = string; + if (preg->regstart != '\0') { + /* We know what char it must start with. */ + while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) { + if (regtry(preg, s)) + return REG_NOERROR; + s++; + } + } + else + /* We don't -- general case. */ + while (1) { + if (regtry(preg, s)) + return REG_NOERROR; + if (*s == '\0') { + break; + } + s += utf8_charlen(*s); + } + + /* Failure. */ + return REG_NOMATCH; +} + +/* + - regtry - try match at specific point + */ + /* 0 failure, 1 success */ +static int regtry( regex_t *preg, const char *string ) +{ + int i; + + preg->reginput = string; + + for (i = 0; i < preg->nmatch; i++) { + preg->pmatch[i].rm_so = -1; + preg->pmatch[i].rm_eo = -1; + } + if (regmatch(preg, 1)) { + preg->pmatch[0].rm_so = string - preg->start; + preg->pmatch[0].rm_eo = preg->reginput - preg->start; + return(1); + } else + return(0); +} + +/** + * Returns bytes matched if 'pattern' is a prefix of 'string'. + * + * If 'nocase' is non-zero, does a case-insensitive match. + * + * Returns -1 on not found. + */ +static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase) +{ + const char *s = string; + while (proglen && *s) { + int ch; + int n = reg_utf8_tounicode_case(s, &ch, nocase); + if (ch != *prog) { + return -1; + } + prog++; + s += n; + proglen--; + } + if (proglen == 0) { + return s - string; + } + return -1; +} + +/** + * Searchs for 'c' in the range 'range'. + * + * Returns 1 if found, or 0 if not. + */ +static int reg_range_find(const int *range, int c) +{ + while (*range) { + /*printf("Checking %d in range [%d,%d]\n", c, range[1], (range[0] + range[1] - 1));*/ + if (c >= range[1] && c <= (range[0] + range[1] - 1)) { + return 1; + } + range += 2; + } + return 0; +} + +/** + * Search for the character 'c' in the utf-8 string 'string'. + * + * If 'nocase' is set, the 'string' is assumed to be uppercase + * and 'c' is converted to uppercase before matching. + * + * Returns the byte position in the string where the 'c' was found, or + * NULL if not found. + */ +static const char *str_find(const char *string, int c, int nocase) +{ + if (nocase) { + /* The "string" should already be converted to uppercase */ + c = utf8_upper(c); + } + while (*string) { + int ch; + int n = reg_utf8_tounicode_case(string, &ch, nocase); + if (c == ch) { + return string; + } + string += n; + } + return NULL; +} + +/** + * Returns true if 'ch' is an end-of-line char. + * + * In REG_NEWLINE mode, \n is considered EOL in + * addition to \0 + */ +static int reg_iseol(regex_t *preg, int ch) +{ + if (preg->cflags & REG_NEWLINE) { + return ch == '\0' || ch == '\n'; + } + else { + return ch == '\0'; + } +} + +static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin) +{ + int nextch = '\0'; + const char *save; + int no; + int c; + + int max = preg->program[scan + 2]; + int min = preg->program[scan + 3]; + int next = regnext(preg, scan); + + /* + * Lookahead to avoid useless match attempts + * when we know what character comes next. + */ + if (OP(preg, next) == EXACTLY) { + nextch = preg->program[OPERAND(next)]; + } + save = preg->reginput; + no = regrepeat(preg, scan + 5, max); + if (no < min) { + return 0; + } + if (matchmin) { + /* from min up to no */ + max = no; + no = min; + } + /* else from no down to min */ + while (1) { + if (matchmin) { + if (no > max) { + break; + } + } + else { + if (no < min) { + break; + } + } + preg->reginput = save + utf8_index(save, no); + reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE)); + /* If it could work, try it. */ + if (reg_iseol(preg, nextch) || c == nextch) { + if (regmatch(preg, next)) { + return(1); + } + } + if (matchmin) { + /* Couldn't or didn't, add one more */ + no++; + } + else { + /* Couldn't or didn't -- back up. */ + no--; + } + } + return(0); +} + +static int regmatchrepeat(regex_t *preg, int scan, int matchmin) +{ + int *scanpt = preg->program + scan; + + int max = scanpt[2]; + int min = scanpt[3]; + + /* Have we reached min? */ + if (scanpt[4] < min) { + /* No, so get another one */ + scanpt[4]++; + if (regmatch(preg, scan + 5)) { + return 1; + } + scanpt[4]--; + return 0; + } + if (scanpt[4] > max) { + return 0; + } + + if (matchmin) { + /* minimal, so try other branch first */ + if (regmatch(preg, regnext(preg, scan))) { + return 1; + } + /* No, so try one more */ + scanpt[4]++; + if (regmatch(preg, scan + 5)) { + return 1; + } + scanpt[4]--; + return 0; + } + /* maximal, so try this branch again */ + if (scanpt[4] < max) { + scanpt[4]++; + if (regmatch(preg, scan + 5)) { + return 1; + } + scanpt[4]--; + } + /* At this point we are at max with no match. Try the other branch */ + return regmatch(preg, regnext(preg, scan)); +} + +/* + - regmatch - main matching routine + * + * Conceptually the strategy is simple: check to see whether the current + * node matches, call self recursively to see whether the rest matches, + * and then act accordingly. In practice we make some effort to avoid + * recursion, in particular by going through "ordinary" nodes (that don't + * need to know whether the rest of the match failed) by a loop instead of + * by recursion. + */ +/* 0 failure, 1 success */ +static int regmatch(regex_t *preg, int prog) +{ + int scan; /* Current node. */ + int next; /* Next node. */ + + scan = prog; + +#ifdef DEBUG + if (scan != 0 && regnarrate) + fprintf(stderr, "%s(\n", regprop(scan)); +#endif + while (scan != 0) { + int n; + int c; +#ifdef DEBUG + if (regnarrate) { + fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan))); /* Where, what. */ + } +#endif + next = regnext(preg, scan); + n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE)); + + switch (OP(preg, scan)) { + case BOL: + if (preg->reginput != preg->regbol) + return(0); + break; + case EOL: + if (!reg_iseol(preg, c)) { + return(0); + } + break; + case WORDA: + /* Must be looking at a letter, digit, or _ */ + if ((!isalnum(UCHAR(c))) && c != '_') + return(0); + /* Prev must be BOL or nonword */ + if (preg->reginput > preg->regbol && + (isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_')) + return(0); + break; + case WORDZ: + /* Can't match at BOL */ + if (preg->reginput > preg->regbol) { + /* Current must be EOL or nonword */ + if (reg_iseol(preg, c) || !isalnum(UCHAR(c)) || c != '_') { + c = preg->reginput[-1]; + /* Previous must be word */ + if (isalnum(UCHAR(c)) || c == '_') { + break; + } + } + } + /* No */ + return(0); + + case ANY: + if (reg_iseol(preg, c)) + return 0; + preg->reginput += n; + break; + case EXACTLY: { + int opnd; + int len; + int slen; + + opnd = OPERAND(scan); + len = str_int_len(preg->program + opnd); + + slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE); + if (slen < 0) { + return(0); + } + preg->reginput += slen; + } + break; + case ANYOF: + if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) { + return(0); + } + preg->reginput += n; + break; + case ANYBUT: + if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) { + return(0); + } + preg->reginput += n; + break; + case NOTHING: + break; + case BACK: + break; + case BRANCH: { + const char *save; + + if (OP(preg, next) != BRANCH) /* No choice. */ + next = OPERAND(scan); /* Avoid recursion. */ + else { + do { + save = preg->reginput; + if (regmatch(preg, OPERAND(scan))) { + return(1); + } + preg->reginput = save; + scan = regnext(preg, scan); + } while (scan != 0 && OP(preg, scan) == BRANCH); + return(0); + /* NOTREACHED */ + } + } + break; + case REP: + case REPMIN: + return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN); + + case REPX: + case REPXMIN: + return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN); + + case END: + return(1); /* Success! */ + break; + default: + if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) { + const char *save; + + save = preg->reginput; + + if (regmatch(preg, next)) { + int no; + /* + * Don't set startp if some later + * invocation of the same parentheses + * already has. + */ + if (OP(preg, scan) < CLOSE) { + no = OP(preg, scan) - OPEN; + if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) { + preg->pmatch[no].rm_so = save - preg->start; + } + } + else { + no = OP(preg, scan) - CLOSE; + if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) { + preg->pmatch[no].rm_eo = save - preg->start; + } + } + return(1); + } else + return(0); + } + return REG_ERR_INTERNAL; + } + + scan = next; + } + + /* + * We get here only if there's trouble -- normally "case END" is + * the terminating point. + */ + return REG_ERR_INTERNAL; +} + +/* + - regrepeat - repeatedly match something simple, report how many + */ +static int regrepeat(regex_t *preg, int p, int max) +{ + int count = 0; + const char *scan; + int opnd; + int ch; + int n; + + scan = preg->reginput; + opnd = OPERAND(p); + switch (OP(preg, p)) { + case ANY: + /* No need to handle utf8 specially here */ + while (!reg_iseol(preg, *scan) && count < max) { + count++; + scan++; + } + break; + case EXACTLY: + while (count < max) { + n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); + if (preg->program[opnd] != ch) { + break; + } + count++; + scan += n; + } + break; + case ANYOF: + while (count < max) { + n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); + if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) { + break; + } + count++; + scan += n; + } + break; + case ANYBUT: + while (count < max) { + n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); + if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) { + break; + } + count++; + scan += n; + } + break; + default: /* Oh dear. Called inappropriately. */ + preg->err = REG_ERR_INTERNAL; + count = 0; /* Best compromise. */ + break; + } + preg->reginput = scan; + + return(count); +} + +/* + - regnext - dig the "next" pointer out of a node + */ +static int regnext(regex_t *preg, int p ) +{ + int offset; + + offset = NEXT(preg, p); + + if (offset == 0) + return 0; + + if (OP(preg, p) == BACK) + return(p-offset); + else + return(p+offset); +} + +#if defined(DEBUG) && !defined(JIM_BOOTSTRAP) + +/* + - regdump - dump a regexp onto stdout in vaguely comprehensible form + */ +static void regdump(regex_t *preg) +{ + int s; + int op = EXACTLY; /* Arbitrary non-END op. */ + int next; + char buf[4]; + + int i; + for (i = 1; i < preg->p; i++) { + printf("%02x ", preg->program[i]); + if (i % 16 == 15) { + printf("\n"); + } + } + printf("\n"); + + s = 1; + while (op != END && s < preg->p) { /* While that wasn't END last time... */ + op = OP(preg, s); + printf("%3d: %s", s, regprop(op)); /* Where, what. */ + next = regnext(preg, s); + if (next == 0) /* Next ptr. */ + printf("(0)"); + else + printf("(%d)", next); + s += 2; + if (op == REP || op == REPMIN || op == REPX || op == REPXMIN) { + int max = preg->program[s]; + int min = preg->program[s + 1]; + if (max == 65535) { + printf("{%d,*}", min); + } + else { + printf("{%d,%d}", min, max); + } + printf(" %d", preg->program[s + 2]); + s += 3; + } + else if (op == ANYOF || op == ANYBUT) { + /* set of ranges */ + + while (preg->program[s]) { + int len = preg->program[s++]; + int first = preg->program[s++]; + buf[utf8_fromunicode(buf, first)] = 0; + printf("%s", buf); + if (len > 1) { + buf[utf8_fromunicode(buf, first + len - 1)] = 0; + printf("-%s", buf); + } + } + s++; + } + else if (op == EXACTLY) { + /* Literal string, where present. */ + + while (preg->program[s]) { + buf[utf8_fromunicode(buf, preg->program[s])] = 0; + printf("%s", buf); + s++; + } + s++; + } + putchar('\n'); + } + + if (op == END) { + /* Header fields of interest. */ + if (preg->regstart) { + buf[utf8_fromunicode(buf, preg->regstart)] = 0; + printf("start '%s' ", buf); + } + if (preg->reganch) + printf("anchored "); + if (preg->regmust != 0) { + int i; + printf("must have:"); + for (i = 0; i < preg->regmlen; i++) { + putchar(preg->program[preg->regmust + i]); + } + putchar('\n'); + } + } + printf("\n"); +} + +/* + - regprop - printable representation of opcode + */ +static const char *regprop( int op ) +{ + static char buf[50]; + + switch (op) { + case BOL: + return "BOL"; + case EOL: + return "EOL"; + case ANY: + return "ANY"; + case ANYOF: + return "ANYOF"; + case ANYBUT: + return "ANYBUT"; + case BRANCH: + return "BRANCH"; + case EXACTLY: + return "EXACTLY"; + case NOTHING: + return "NOTHING"; + case BACK: + return "BACK"; + case END: + return "END"; + case REP: + return "REP"; + case REPMIN: + return "REPMIN"; + case REPX: + return "REPX"; + case REPXMIN: + return "REPXMIN"; + case WORDA: + return "WORDA"; + case WORDZ: + return "WORDZ"; + default: + if (op >= OPEN && op < CLOSE) { + snprintf(buf, sizeof(buf), "OPEN%d", op-OPEN); + } + else if (op >= CLOSE && op < CLOSE_END) { + snprintf(buf, sizeof(buf), "CLOSE%d", op-CLOSE); + } + else { + snprintf(buf, sizeof(buf), "?%d?\n", op); + } + return(buf); + } +} +#endif /* JIM_BOOTSTRAP */ + +size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) +{ + static const char *error_strings[] = { + "success", + "no match", + "bad pattern", + "null argument", + "unknown error", + "too big", + "out of memory", + "too many ()", + "parentheses () not balanced", + "braces {} not balanced", + "invalid repetition count(s)", + "extra characters", + "*+ of empty atom", + "nested count", + "internal error", + "count follows nothing", + "trailing backslash", + "corrupted program", + "contains null char", + }; + const char *err; + + if (errcode < 0 || errcode >= REG_ERR_NUM) { + err = "Bad error code"; + } + else { + err = error_strings[errcode]; + } + + return snprintf(errbuf, errbuf_size, "%s", err); +} + +void regfree(regex_t *preg) +{ + free(preg->program); +} + +#endif diff --git a/release/src/router/usbmodeswitch/jim/jimregexp.h b/release/src/router/usbmodeswitch/jim/jimregexp.h new file mode 100644 index 0000000000..79a87e5f68 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jimregexp.h @@ -0,0 +1,117 @@ +#ifndef JIMREGEXP_H +#define JIMREGEXP_H + +#ifndef _JIMAUTOCONF_H +#error Need jimautoconf.h +#endif + +#if defined(HAVE_REGCOMP) && !defined(JIM_REGEXP) +/* Use POSIX regex */ +#include + +#else + +#include + +/* + * Definitions etc. for regexp(3) routines. + * + * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], + * not the System V one. + * + * 11/04/02 (seiwald) - const-ing for string literals + */ + +typedef struct { + int rm_so; + int rm_eo; +} regmatch_t; + +/* + * The "internal use only" fields in regexp.h are present to pass info from + * compile to execute that permits the execute phase to run lots faster on + * simple cases. They are: + * + * regstart char that must begin a match; '\0' if none obvious + * reganch is the match anchored (at beginning-of-line only)? + * regmust string (pointer into program) that match must include, or NULL + * regmlen length of regmust string + * + * Regstart and reganch permit very fast decisions on suitable starting points + * for a match, cutting down the work a lot. Regmust permits fast rejection + * of lines that cannot possibly match. The regmust tests are costly enough + * that regcomp() supplies a regmust only if the r.e. contains something + * potentially expensive (at present, the only such thing detected is * or + + * at the start of the r.e., which can involve a lot of backup). Regmlen is + * supplied because the test in regexec() needs it and regcomp() is computing + * it anyway. + */ + +typedef struct regexp { + /* -- public -- */ + int re_nsub; /* number of parenthesized subexpressions */ + + /* -- private -- */ + int cflags; /* Flags used when compiling */ + int err; /* Any error which occurred during compile */ + int regstart; /* Internal use only. */ + int reganch; /* Internal use only. */ + int regmust; /* Internal use only. */ + int regmlen; /* Internal use only. */ + int *program; /* Allocated */ + + /* working state - compile */ + const char *regparse; /* Input-scan pointer. */ + int p; /* Current output pos in program */ + int proglen; /* Allocated program size */ + + /* working state - exec */ + int eflags; /* Flags used when executing */ + const char *start; /* Initial string pointer. */ + const char *reginput; /* Current input pointer. */ + const char *regbol; /* Beginning of input, for ^ check. */ + + /* Input to regexec() */ + regmatch_t *pmatch; /* submatches will be stored here */ + int nmatch; /* size of pmatch[] */ +} regexp; + +typedef regexp regex_t; + +#define REG_EXTENDED 0 +#define REG_NEWLINE 1 +#define REG_ICASE 2 + +#define REG_NOTBOL 16 + +enum { + REG_NOERROR, /* Success. */ + REG_NOMATCH, /* Didn't find a match (for regexec). */ + REG_BADPAT, /* >= REG_BADPAT is an error */ + REG_ERR_NULL_ARGUMENT, + REG_ERR_UNKNOWN, + REG_ERR_TOO_BIG, + REG_ERR_NOMEM, + REG_ERR_TOO_MANY_PAREN, + REG_ERR_UNMATCHED_PAREN, + REG_ERR_UNMATCHED_BRACES, + REG_ERR_BAD_COUNT, + REG_ERR_JUNK_ON_END, + REG_ERR_OPERAND_COULD_BE_EMPTY, + REG_ERR_NESTED_COUNT, + REG_ERR_INTERNAL, + REG_ERR_COUNT_FOLLOWS_NOTHING, + REG_ERR_TRAILING_BACKSLASH, + REG_ERR_CORRUPTED, + REG_ERR_NULL_CHAR, + REG_ERR_NUM +}; + +int regcomp(regex_t *preg, const char *regex, int cflags); +int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags); +size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size); +void regfree(regex_t *preg); + +#endif + +#endif diff --git a/release/src/router/usbmodeswitch/jim/jimsh.c b/release/src/router/usbmodeswitch/jim/jimsh.c new file mode 100644 index 0000000000..2c94468dd7 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/jimsh.c @@ -0,0 +1,111 @@ + +/* Jimsh - An interactive shell for Jim + * Copyright 2005 Salvatore Sanfilippo + * Copyright 2009 Steve Bennett + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * A copy of the license is also included in the source distribution + * of Jim, as a TXT file name called LICENSE. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "jim.h" +#include "jimautoconf.h" + +/* From initjimsh.tcl */ +extern int Jim_initjimshInit(Jim_Interp *interp); + +static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[]) +{ + int n; + Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); + + /* Populate argv global var */ + for (n = 0; n < argc; n++) { + Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1); + + Jim_ListAppendElement(interp, listObj, obj); + } + + Jim_SetVariableStr(interp, "argv", listObj); + Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc)); +} + +int main(int argc, char *const argv[]) +{ + int retcode; + Jim_Interp *interp; + + if (argc > 1 && strcmp(argv[1], "--version") == 0) { + printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100); + return 0; + } + + /* Create and initialize the interpreter */ + interp = Jim_CreateInterp(); + Jim_RegisterCoreCommands(interp); + + /* Register static extensions */ + if (Jim_InitStaticExtensions(interp) != JIM_OK) { + Jim_MakeErrorMessage(interp); + fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp))); + } + + Jim_SetVariableStrWithStr(interp, "jim_argv0", argv[0]); + Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0"); + retcode = Jim_initjimshInit(interp); + + if (argc == 1) { + if (retcode == JIM_ERR) { + Jim_MakeErrorMessage(interp); + fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp))); + } + if (retcode != JIM_EXIT) { + JimSetArgv(interp, 0, NULL); + retcode = Jim_InteractivePrompt(interp); + } + } + else { + if (argc > 2 && strcmp(argv[1], "-e") == 0) { + JimSetArgv(interp, argc - 3, argv + 3); + retcode = Jim_Eval(interp, argv[2]); + if (retcode != JIM_ERR) { + printf("%s\n", Jim_String(Jim_GetResult(interp))); + } + } + else { + Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1)); + JimSetArgv(interp, argc - 2, argv + 2); + retcode = Jim_EvalFile(interp, argv[1]); + } + if (retcode == JIM_ERR) { + Jim_MakeErrorMessage(interp); + fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp))); + } + } + if (retcode == JIM_EXIT) { + retcode = Jim_GetExitCode(interp); + } + else if (retcode == JIM_ERR) { + retcode = 1; + } + else { + retcode = 0; + } + Jim_FreeInterp(interp); + return retcode; +} diff --git a/release/src/router/usbmodeswitch/jim/linenoise.c b/release/src/router/usbmodeswitch/jim/linenoise.c new file mode 100644 index 0000000000..4065d06a7b --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/linenoise.c @@ -0,0 +1,1379 @@ +/* linenoise.c -- guerrilla line editing library against the idea that a + * line editing lib needs to be 20,000 lines of C code. + * + * You can find the latest source code at: + * + * http://github.com/antirez/linenoise + * + * Does a number of crazy assumptions that happen to be true in 99.9999% of + * the 2010 UNIX computers around. + * + * ------------------------------------------------------------------------ + * + * Copyright (c) 2010, Salvatore Sanfilippo + * Copyright (c) 2010, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ------------------------------------------------------------------------ + * + * References: + * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html + * + * Todo list: + * - Win32 support + * - Save and load history containing newlines + * + * Bloat: + * - Completion? + * + * List of escape sequences used by this program, we do everything just + * a few sequences. In order to be so cheap we may have some + * flickering effect with some slow terminal, but the lesser sequences + * the more compatible. + * + * CHA (Cursor Horizontal Absolute) + * Sequence: ESC [ n G + * Effect: moves cursor to column n (1 based) + * + * EL (Erase Line) + * Sequence: ESC [ n K + * Effect: if n is 0 or missing, clear from cursor to end of line + * Effect: if n is 1, clear from beginning of line to cursor + * Effect: if n is 2, clear entire line + * + * CUF (CUrsor Forward) + * Sequence: ESC [ n C + * Effect: moves cursor forward of n chars + * + * The following are used to clear the screen: ESC [ H ESC [ 2 J + * This is actually composed of two sequences: + * + * cursorhome + * Sequence: ESC [ H + * Effect: moves the cursor to upper left corner + * + * ED2 (Clear entire screen) + * Sequence: ESC [ 2 J + * Effect: clear the whole screen + * + * == For highlighting control characters, we also use the following two == + * SO (enter StandOut) + * Sequence: ESC [ 7 m + * Effect: Uses some standout mode such as reverse video + * + * SE (Standout End) + * Sequence: ESC [ 0 m + * Effect: Exit standout mode + * + * == Only used if TIOCGWINSZ fails == + * DSR/CPR (Report cursor position) + * Sequence: ESC [ 6 n + * Effect: reports current cursor position as ESC [ NNN ; MMM R + */ + +#ifdef __MINGW32__ +#include +#include +#define USE_WINCONSOLE +#else +#include +#include +#include +#define USE_TERMIOS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "linenoise.h" + +#include "jim-config.h" +#ifdef JIM_UTF8 +#define USE_UTF8 +#endif +#include "utf8.h" + +#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 +#define LINENOISE_MAX_LINE 4096 + +#define ctrl(C) ((C) - '@') + +/* Use -ve numbers here to co-exist with normal unicode chars */ +enum { + SPECIAL_NONE, + SPECIAL_UP = -20, + SPECIAL_DOWN = -21, + SPECIAL_LEFT = -22, + SPECIAL_RIGHT = -23, + SPECIAL_DELETE = -24, + SPECIAL_HOME = -25, + SPECIAL_END = -26, +}; + +static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; +static int history_len = 0; +static char **history = NULL; + +/* Structure to contain the status of the current (being edited) line */ +struct current { + char *buf; /* Current buffer. Always null terminated */ + int bufmax; /* Size of the buffer, including space for the null termination */ + int len; /* Number of bytes in 'buf' */ + int chars; /* Number of chars in 'buf' (utf-8 chars) */ + int pos; /* Cursor position, measured in chars */ + int cols; /* Size of the window, in chars */ + const char *prompt; +#if defined(USE_TERMIOS) + int fd; /* Terminal fd */ +#elif defined(USE_WINCONSOLE) + HANDLE outh; /* Console output handle */ + HANDLE inh; /* Console input handle */ + int rows; /* Screen rows */ + int x; /* Current column during output */ + int y; /* Current row */ +#endif +}; + +static int fd_read(struct current *current); +static int getWindowSize(struct current *current); + +void linenoiseHistoryFree(void) { + if (history) { + int j; + + for (j = 0; j < history_len; j++) + free(history[j]); + free(history); + history = NULL; + } +} + +#if defined(USE_TERMIOS) +static void linenoiseAtExit(void); +static struct termios orig_termios; /* in order to restore at exit */ +static int rawmode = 0; /* for atexit() function to check if restore is needed*/ +static int atexit_registered = 0; /* register atexit just 1 time */ + +static const char *unsupported_term[] = {"dumb","cons25",NULL}; + +static int isUnsupportedTerm(void) { + char *term = getenv("TERM"); + + if (term) { + int j; + for (j = 0; unsupported_term[j]; j++) { + if (strcasecmp(term, unsupported_term[j]) == 0) { + return 1; + } + } + } + return 0; +} + +static int enableRawMode(struct current *current) { + struct termios raw; + + current->fd = STDIN_FILENO; + + if (!isatty(current->fd) || isUnsupportedTerm() || + tcgetattr(current->fd, &orig_termios) == -1) { +fatal: + errno = ENOTTY; + return -1; + } + + if (!atexit_registered) { + atexit(linenoiseAtExit); + atexit_registered = 1; + } + + raw = orig_termios; /* modify the original mode */ + /* input modes: no break, no CR to NL, no parity check, no strip char, + * no start/stop output control. */ + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + /* output modes - disable post processing */ + raw.c_oflag &= ~(OPOST); + /* control modes - set 8 bit chars */ + raw.c_cflag |= (CS8); + /* local modes - choing off, canonical off, no extended functions, + * no signal chars (^Z,^C) */ + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + /* control chars - set return condition: min number of bytes and timer. + * We want read to return every single byte, without timeout. */ + raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ + + /* put terminal in raw mode after flushing */ + if (tcsetattr(current->fd,TCSADRAIN,&raw) < 0) { + goto fatal; + } + rawmode = 1; + + current->cols = 0; + return 0; +} + +static void disableRawMode(struct current *current) { + /* Don't even check the return value as it's too late. */ + if (rawmode && tcsetattr(current->fd,TCSADRAIN,&orig_termios) != -1) + rawmode = 0; +} + +/* At exit we'll try to fix the terminal to the initial conditions. */ +static void linenoiseAtExit(void) { + if (rawmode) { + tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios); + } + linenoiseHistoryFree(); +} + +/* gcc/glibc insists that we care about the return code of write! */ +#define IGNORE_RC(EXPR) if (EXPR) {} + +/* This is fdprintf() on some systems, but use a different + * name to avoid conflicts + */ +static void fd_printf(int fd, const char *format, ...) +{ + va_list args; + char buf[64]; + int n; + + va_start(args, format); + n = vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + IGNORE_RC(write(fd, buf, n)); +} + +static void clearScreen(struct current *current) +{ + fd_printf(current->fd, "\x1b[H\x1b[2J"); +} + +static void cursorToLeft(struct current *current) +{ + fd_printf(current->fd, "\x1b[1G"); +} + +static int outputChars(struct current *current, const char *buf, int len) +{ + return write(current->fd, buf, len); +} + +static void outputControlChar(struct current *current, char ch) +{ + fd_printf(current->fd, "\033[7m^%c\033[0m", ch); +} + +static void eraseEol(struct current *current) +{ + fd_printf(current->fd, "\x1b[0K"); +} + +static void setCursorPos(struct current *current, int x) +{ + fd_printf(current->fd, "\x1b[1G\x1b[%dC", x); +} + +/** + * Reads a char from 'fd', waiting at most 'timeout' milliseconds. + * + * A timeout of -1 means to wait forever. + * + * Returns -1 if no char is received within the time or an error occurs. + */ +static int fd_read_char(int fd, int timeout) +{ + struct pollfd p; + unsigned char c; + + p.fd = fd; + p.events = POLLIN; + + if (poll(&p, 1, timeout) == 0) { + /* timeout */ + return -1; + } + if (read(fd, &c, 1) != 1) { + return -1; + } + return c; +} + +/** + * Reads a complete utf-8 character + * and returns the unicode value, or -1 on error. + */ +static int fd_read(struct current *current) +{ +#ifdef USE_UTF8 + char buf[4]; + int n; + int i; + int c; + + if (read(current->fd, &buf[0], 1) != 1) { + return -1; + } + n = utf8_charlen(buf[0]); + if (n < 1 || n > 3) { + return -1; + } + for (i = 1; i < n; i++) { + if (read(current->fd, &buf[i], 1) != 1) { + return -1; + } + } + buf[n] = 0; + /* decode and return the character */ + utf8_tounicode(buf, &c); + return c; +#else + return fd_read_char(current->fd, -1); +#endif +} + +static int getWindowSize(struct current *current) +{ + struct winsize ws; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col != 0) { + current->cols = ws.ws_col; + return 0; + } + + /* Failed to query the window size. Perhaps we are on a serial terminal. + * Try to query the width by sending the cursor as far to the right + * and reading back the cursor position. + * Note that this is only done once per call to linenoise rather than + * every time the line is refreshed for efficiency reasons. + */ + if (current->cols == 0) { + current->cols = 80; + + /* Move cursor far right and report cursor position */ + fd_printf(current->fd, "\x1b[999G" "\x1b[6n"); + + /* Parse the response: ESC [ rows ; cols R */ + if (fd_read_char(current->fd, 100) == 0x1b && fd_read_char(current->fd, 100) == '[') { + int n = 0; + while (1) { + int ch = fd_read_char(current->fd, 100); + if (ch == ';') { + /* Ignore rows */ + n = 0; + } + else if (ch == 'R') { + /* Got cols */ + if (n != 0 && n < 1000) { + current->cols = n; + } + break; + } + else if (ch >= 0 && ch <= '9') { + n = n * 10 + ch - '0'; + } + else { + break; + } + } + } + } + return 0; +} + +/** + * If escape (27) was received, reads subsequent + * chars to determine if this is a known special key. + * + * Returns SPECIAL_NONE if unrecognised, or -1 if EOF. + * + * If no additional char is received within a short time, + * 27 is returned. + */ +static int check_special(int fd) +{ + int c = fd_read_char(fd, 50); + int c2; + + if (c < 0) { + return 27; + } + + c2 = fd_read_char(fd, 50); + if (c2 < 0) { + return c2; + } + if (c == '[' || c == 'O') { + /* Potential arrow key */ + switch (c2) { + case 'A': + return SPECIAL_UP; + case 'B': + return SPECIAL_DOWN; + case 'C': + return SPECIAL_RIGHT; + case 'D': + return SPECIAL_LEFT; + case 'F': + return SPECIAL_END; + case 'H': + return SPECIAL_HOME; + } + } + if (c == '[' && c2 >= '1' && c2 <= '6') { + /* extended escape */ + int c3 = fd_read_char(fd, 50); + if (c2 == '3' && c3 == '~') { + /* delete char under cursor */ + return SPECIAL_DELETE; + } + while (c3 != -1 && c3 != '~') { + /* .e.g \e[12~ or '\e[11;2~ discard the complete sequence */ + c3 = fd_read_char(fd, 50); + } + } + + return SPECIAL_NONE; +} +#elif defined(USE_WINCONSOLE) + +static DWORD orig_consolemode = 0; + +static int enableRawMode(struct current *current) { + DWORD n; + INPUT_RECORD irec; + + current->outh = GetStdHandle(STD_OUTPUT_HANDLE); + current->inh = GetStdHandle(STD_INPUT_HANDLE); + + if (!PeekConsoleInput(current->inh, &irec, 1, &n)) { + return -1; + } + if (getWindowSize(current) != 0) { + return -1; + } + if (GetConsoleMode(current->inh, &orig_consolemode)) { + SetConsoleMode(current->inh, ENABLE_PROCESSED_INPUT); + } + return 0; +} + +static void disableRawMode(struct current *current) +{ + SetConsoleMode(current->inh, orig_consolemode); +} + +static void clearScreen(struct current *current) +{ + COORD topleft = { 0, 0 }; + DWORD n; + + FillConsoleOutputCharacter(current->outh, ' ', + current->cols * current->rows, topleft, &n); + FillConsoleOutputAttribute(current->outh, + FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, + current->cols * current->rows, topleft, &n); + SetConsoleCursorPosition(current->outh, topleft); +} + +static void cursorToLeft(struct current *current) +{ + COORD pos = { 0, current->y }; + DWORD n; + + FillConsoleOutputAttribute(current->outh, + FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, current->cols, pos, &n); + current->x = 0; +} + +static int outputChars(struct current *current, const char *buf, int len) +{ + COORD pos = { current->x, current->y }; + WriteConsoleOutputCharacter(current->outh, buf, len, pos, 0); + current->x += len; + return 0; +} + +static void outputControlChar(struct current *current, char ch) +{ + COORD pos = { current->x, current->y }; + DWORD n; + + FillConsoleOutputAttribute(current->outh, BACKGROUND_INTENSITY, 2, pos, &n); + outputChars(current, "^", 1); + outputChars(current, &ch, 1); +} + +static void eraseEol(struct current *current) +{ + COORD pos = { current->x, current->y }; + DWORD n; + + FillConsoleOutputCharacter(current->outh, ' ', current->cols - current->x, pos, &n); +} + +static void setCursorPos(struct current *current, int x) +{ + COORD pos = { x, current->y }; + + SetConsoleCursorPosition(current->outh, pos); + current->x = x; +} + +static int fd_read(struct current *current) +{ + while (1) { + INPUT_RECORD irec; + DWORD n; + if (WaitForSingleObject(current->inh, INFINITE) != WAIT_OBJECT_0) { + break; + } + if (!ReadConsoleInput (current->inh, &irec, 1, &n)) { + break; + } + if (irec.EventType == KEY_EVENT && irec.Event.KeyEvent.bKeyDown) { + KEY_EVENT_RECORD *k = &irec.Event.KeyEvent; + if (k->dwControlKeyState & ENHANCED_KEY) { + switch (k->wVirtualKeyCode) { + case VK_LEFT: + return SPECIAL_LEFT; + case VK_RIGHT: + return SPECIAL_RIGHT; + case VK_UP: + return SPECIAL_UP; + case VK_DOWN: + return SPECIAL_DOWN; + case VK_DELETE: + return SPECIAL_DELETE; + case VK_HOME: + return SPECIAL_HOME; + case VK_END: + return SPECIAL_END; + } + } + /* Note that control characters are already translated in AsciiChar */ + else { +#ifdef USE_UTF8 + return k->uChar.UnicodeChar; +#else + return k->uChar.AsciiChar; +#endif + } + } + } + return -1; +} + +static int getWindowSize(struct current *current) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + if (!GetConsoleScreenBufferInfo(current->outh, &info)) { + return -1; + } + current->cols = info.dwSize.X; + current->rows = info.dwSize.Y; + if (current->cols <= 0 || current->rows <= 0) { + current->cols = 80; + return -1; + } + current->y = info.dwCursorPosition.Y; + current->x = info.dwCursorPosition.X; + return 0; +} +#endif + +static int utf8_getchars(char *buf, int c) +{ +#ifdef USE_UTF8 + return utf8_fromunicode(buf, c); +#else + *buf = c; + return 1; +#endif +} + +/** + * Returns the unicode character at the given offset, + * or -1 if none. + */ +static int get_char(struct current *current, int pos) +{ + if (pos >= 0 && pos < current->chars) { + int c; + int i = utf8_index(current->buf, pos); + (void)utf8_tounicode(current->buf + i, &c); + return c; + } + return -1; +} + +static void refreshLine(const char *prompt, struct current *current) +{ + int plen; + int pchars; + int backup = 0; + int i; + const char *buf = current->buf; + int chars = current->chars; + int pos = current->pos; + int b; + int ch; + int n; + + /* Should intercept SIGWINCH. For now, just get the size every time */ + getWindowSize(current); + + plen = strlen(prompt); + pchars = utf8_strlen(prompt, plen); + + /* Account for a line which is too long to fit in the window. + * Note that control chars require an extra column + */ + + /* How many cols are required to the left of 'pos'? + * The prompt, plus one extra for each control char + */ + n = pchars + utf8_strlen(buf, current->len); + b = 0; + for (i = 0; i < pos; i++) { + b += utf8_tounicode(buf + b, &ch); + if (ch < ' ') { + n++; + } + } + + /* If too many are need, strip chars off the front of 'buf' + * until it fits. Note that if the current char is a control character, + * we need one extra col. + */ + if (current->pos < current->chars && get_char(current, current->pos) < ' ') { + n++; + } + + while (n >= current->cols) { + b = utf8_tounicode(buf, &ch); + if (ch < ' ') { + n--; + } + n--; + buf += b; + pos--; + chars--; + } + + /* Cursor to left edge, then the prompt */ + cursorToLeft(current); + outputChars(current, prompt, plen); + + /* Now the current buffer content */ + + /* Need special handling for control characters. + * If we hit 'cols', stop. + */ + b = 0; /* unwritted bytes */ + n = 0; /* How many control chars were written */ + for (i = 0; i < chars; i++) { + int ch; + int w = utf8_tounicode(buf + b, &ch); + if (ch < ' ') { + n++; + } + if (pchars + i + n >= current->cols) { + break; + } + if (ch < ' ') { + /* A control character, so write the buffer so far */ + outputChars(current, buf, b); + buf += b + w; + b = 0; + outputControlChar(current, ch + '@'); + if (i < pos) { + backup++; + } + } + else { + b += w; + } + } + outputChars(current, buf, b); + + /* Erase to right, move cursor to original position */ + eraseEol(current); + setCursorPos(current, pos + pchars + backup); +} + +static void set_current(struct current *current, const char *str) +{ + strncpy(current->buf, str, current->bufmax); + current->buf[current->bufmax - 1] = 0; + current->len = strlen(current->buf); + current->pos = current->chars = utf8_strlen(current->buf, current->len); +} + +static int has_room(struct current *current, int bytes) +{ + return current->len + bytes < current->bufmax - 1; +} + +/** + * Removes the char at 'pos'. + * + * Returns 1 if the line needs to be refreshed, 2 if not + * and 0 if nothing was removed + */ +static int remove_char(struct current *current, int pos) +{ + if (pos >= 0 && pos < current->chars) { + int p1, p2; + int ret = 1; + p1 = utf8_index(current->buf, pos); + p2 = p1 + utf8_index(current->buf + p1, 1); + +#ifdef USE_TERMIOS + /* optimise remove char in the case of removing the last char */ + if (current->pos == pos + 1 && current->pos == current->chars) { + if (current->buf[pos] >= ' ' && utf8_strlen(current->prompt, -1) + utf8_strlen(current->buf, current->len) < current->cols - 1) { + ret = 2; + fd_printf(current->fd, "\b \b"); + } + } +#endif + + /* Move the null char too */ + memmove(current->buf + p1, current->buf + p2, current->len - p2 + 1); + current->len -= (p2 - p1); + current->chars--; + + if (current->pos > pos) { + current->pos--; + } + return ret; + } + return 0; +} + +/** + * Insert 'ch' at position 'pos' + * + * Returns 1 if the line needs to be refreshed, 2 if not + * and 0 if nothing was inserted (no room) + */ +static int insert_char(struct current *current, int pos, int ch) +{ + char buf[3]; + int n = utf8_getchars(buf, ch); + + if (has_room(current, n) && pos >= 0 && pos <= current->chars) { + int p1, p2; + int ret = 1; + p1 = utf8_index(current->buf, pos); + p2 = p1 + n; + +#ifdef USE_TERMIOS + /* optimise the case where adding a single char to the end and no scrolling is needed */ + if (current->pos == pos && current->chars == pos) { + if (ch >= ' ' && utf8_strlen(current->prompt, -1) + utf8_strlen(current->buf, current->len) < current->cols - 1) { + IGNORE_RC(write(current->fd, buf, n)); + ret = 2; + } + } +#endif + + memmove(current->buf + p2, current->buf + p1, current->len - p1); + memcpy(current->buf + p1, buf, n); + current->len += n; + + current->chars++; + if (current->pos >= pos) { + current->pos++; + } + return ret; + } + return 0; +} + +/** + * Returns 0 if no chars were removed or non-zero otherwise. + */ +static int remove_chars(struct current *current, int pos, int n) +{ + int removed = 0; + while (n-- && remove_char(current, pos)) { + removed++; + } + return removed; +} + +#ifndef NO_COMPLETION +static linenoiseCompletionCallback *completionCallback = NULL; + +static void beep() { +#ifdef USE_TERMIOS + fprintf(stderr, "\x7"); + fflush(stderr); +#endif +} + +static void freeCompletions(linenoiseCompletions *lc) { + size_t i; + for (i = 0; i < lc->len; i++) + free(lc->cvec[i]); + free(lc->cvec); +} + +static int completeLine(struct current *current) { + linenoiseCompletions lc = { 0, NULL }; + int c = 0; + + completionCallback(current->buf,&lc); + if (lc.len == 0) { + beep(); + } else { + size_t stop = 0, i = 0; + + while(!stop) { + /* Show completion or original buffer */ + if (i < lc.len) { + struct current tmp = *current; + tmp.buf = lc.cvec[i]; + tmp.pos = tmp.len = strlen(tmp.buf); + tmp.chars = utf8_strlen(tmp.buf, tmp.len); + refreshLine(current->prompt, &tmp); + } else { + refreshLine(current->prompt, current); + } + + c = fd_read(current); + if (c == -1) { + break; + } + + switch(c) { + case '\t': /* tab */ + i = (i+1) % (lc.len+1); + if (i == lc.len) beep(); + break; + case 27: /* escape */ + /* Re-show original buffer */ + if (i < lc.len) { + refreshLine(current->prompt, current); + } + stop = 1; + break; + default: + /* Update buffer and return */ + if (i < lc.len) { + set_current(current,lc.cvec[i]); + } + stop = 1; + break; + } + } + } + + freeCompletions(&lc); + return c; /* Return last read character */ +} + +/* Register a callback function to be called for tab-completion. */ +void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { + completionCallback = fn; +} + +void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { + lc->cvec = (char **)realloc(lc->cvec,sizeof(char*)*(lc->len+1)); + lc->cvec[lc->len++] = strdup(str); +} + +#endif + +static int linenoisePrompt(struct current *current) { + int history_index = 0; + + /* The latest history entry is always our current buffer, that + * initially is just an empty string. */ + linenoiseHistoryAdd(""); + + set_current(current, ""); + refreshLine(current->prompt, current); + + while(1) { + int dir = -1; + int c = fd_read(current); + +#ifndef NO_COMPLETION + /* Only autocomplete when the callback is set. It returns < 0 when + * there was an error reading from fd. Otherwise it will return the + * character that should be handled next. */ + if (c == 9 && completionCallback != NULL) { + c = completeLine(current); + /* Return on errors */ + if (c < 0) return current->len; + /* Read next character when 0 */ + if (c == 0) continue; + } +#endif + +process_char: + if (c == -1) return current->len; +#ifdef USE_TERMIOS + if (c == 27) { /* escape sequence */ + c = check_special(current->fd); + } +#endif + switch(c) { + case '\r': /* enter */ + history_len--; + free(history[history_len]); + return current->len; + case ctrl('C'): /* ctrl-c */ + errno = EAGAIN; + return -1; + case 127: /* backspace */ + case ctrl('H'): + if (remove_char(current, current->pos - 1) == 1) { + refreshLine(current->prompt, current); + } + break; + case ctrl('D'): /* ctrl-d */ + if (current->len == 0) { + /* Empty line, so EOF */ + history_len--; + free(history[history_len]); + return -1; + } + /* Otherwise delete char to right of cursor */ + if (remove_char(current, current->pos)) { + refreshLine(current->prompt, current); + } + break; + case ctrl('W'): /* ctrl-w */ + /* eat any spaces on the left */ + { + int pos = current->pos; + while (pos > 0 && get_char(current, pos - 1) == ' ') { + pos--; + } + + /* now eat any non-spaces on the left */ + while (pos > 0 && get_char(current, pos - 1) != ' ') { + pos--; + } + + if (remove_chars(current, pos, current->pos - pos)) { + refreshLine(current->prompt, current); + } + } + break; + case ctrl('R'): /* ctrl-r */ + { + /* Display the reverse-i-search prompt and process chars */ + char rbuf[50]; + char rprompt[80]; + int rchars = 0; + int rlen = 0; + int searchpos = history_len - 1; + + rbuf[0] = 0; + while (1) { + int n = 0; + const char *p = NULL; + int skipsame = 0; + int searchdir = -1; + + snprintf(rprompt, sizeof(rprompt), "(reverse-i-search)'%s': ", rbuf); + refreshLine(rprompt, current); + c = fd_read(current); + if (c == ctrl('H') || c == 127) { + if (rchars) { + int p = utf8_index(rbuf, --rchars); + rbuf[p] = 0; + rlen = strlen(rbuf); + } + continue; + } +#ifdef USE_TERMIOS + if (c == 27) { + c = check_special(current->fd); + } +#endif + if (c == ctrl('P') || c == SPECIAL_UP) { + /* Search for the previous (earlier) match */ + if (searchpos > 0) { + searchpos--; + } + skipsame = 1; + } + else if (c == ctrl('N') || c == SPECIAL_DOWN) { + /* Search for the next (later) match */ + if (searchpos < history_len) { + searchpos++; + } + searchdir = 1; + skipsame = 1; + } + else if (c >= ' ') { + if (rlen >= (int)sizeof(rbuf) + 3) { + continue; + } + + n = utf8_getchars(rbuf + rlen, c); + rlen += n; + rchars++; + rbuf[rlen] = 0; + + /* Adding a new char resets the search location */ + searchpos = history_len - 1; + } + else { + /* Exit from incremental search mode */ + break; + } + + /* Now search through the history for a match */ + for (; searchpos >= 0 && searchpos < history_len; searchpos += searchdir) { + p = strstr(history[searchpos], rbuf); + if (p) { + /* Found a match */ + if (skipsame && strcmp(history[searchpos], current->buf) == 0) { + /* But it is identical, so skip it */ + continue; + } + /* Copy the matching line and set the cursor position */ + set_current(current,history[searchpos]); + current->pos = utf8_strlen(history[searchpos], p - history[searchpos]); + break; + } + } + if (!p && n) { + /* No match, so don't add it */ + rchars--; + rlen -= n; + rbuf[rlen] = 0; + } + } + if (c == ctrl('G') || c == ctrl('C')) { + /* ctrl-g terminates the search with no effect */ + set_current(current, ""); + c = 0; + } + else if (c == ctrl('J')) { + /* ctrl-j terminates the search leaving the buffer in place */ + c = 0; + } + /* Go process the char normally */ + refreshLine(current->prompt, current); + goto process_char; + } + break; + case ctrl('T'): /* ctrl-t */ + if (current->pos > 0 && current->pos < current->chars) { + c = get_char(current, current->pos); + remove_char(current, current->pos); + insert_char(current, current->pos - 1, c); + refreshLine(current->prompt, current); + } + break; + case ctrl('V'): /* ctrl-v */ + if (has_room(current, 3)) { + /* Insert the ^V first */ + if (insert_char(current, current->pos, c)) { + refreshLine(current->prompt, current); + /* Now wait for the next char. Can insert anything except \0 */ + c = fd_read(current); + + /* Remove the ^V first */ + remove_char(current, current->pos - 1); + if (c != -1) { + /* Insert the actual char */ + insert_char(current, current->pos, c); + } + refreshLine(current->prompt, current); + } + } + break; + case ctrl('B'): + case SPECIAL_LEFT: + if (current->pos > 0) { + current->pos--; + refreshLine(current->prompt, current); + } + break; + case ctrl('F'): + case SPECIAL_RIGHT: + if (current->pos < current->chars) { + current->pos++; + refreshLine(current->prompt, current); + } + break; + case ctrl('P'): + case SPECIAL_UP: + dir = 1; + case ctrl('N'): + case SPECIAL_DOWN: + if (history_len > 1) { + /* Update the current history entry before to + * overwrite it with tne next one. */ + free(history[history_len-1-history_index]); + history[history_len-1-history_index] = strdup(current->buf); + /* Show the new entry */ + history_index += dir; + if (history_index < 0) { + history_index = 0; + break; + } else if (history_index >= history_len) { + history_index = history_len-1; + break; + } + set_current(current, history[history_len-1-history_index]); + refreshLine(current->prompt, current); + } + break; + + case SPECIAL_DELETE: + if (remove_char(current, current->pos) == 1) { + refreshLine(current->prompt, current); + } + break; + case SPECIAL_HOME: + current->pos = 0; + refreshLine(current->prompt, current); + break; + case SPECIAL_END: + current->pos = current->chars; + refreshLine(current->prompt, current); + break; + default: + /* Only tab is allowed without ^V */ + if (c == '\t' || c >= ' ') { + if (insert_char(current, current->pos, c) == 1) { + refreshLine(current->prompt, current); + } + } + break; + case ctrl('U'): /* Ctrl+u, delete to beginning of line. */ + if (remove_chars(current, 0, current->pos)) { + refreshLine(current->prompt, current); + } + break; + case ctrl('K'): /* Ctrl+k, delete from current to end of line. */ + if (remove_chars(current, current->pos, current->chars - current->pos)) { + refreshLine(current->prompt, current); + } + break; + case ctrl('A'): /* Ctrl+a, go to the start of the line */ + current->pos = 0; + refreshLine(current->prompt, current); + break; + case ctrl('E'): /* ctrl+e, go to the end of the line */ + current->pos = current->chars; + refreshLine(current->prompt, current); + break; + case ctrl('L'): /* Ctrl+L, clear screen */ + /* clear screen */ + clearScreen(current); + /* Force recalc of window size for serial terminals */ + current->cols = 0; + refreshLine(current->prompt, current); + break; + } + } + return current->len; +} + +char *linenoise(const char *prompt) +{ + int count; + struct current current; + char buf[LINENOISE_MAX_LINE]; + + if (enableRawMode(¤t) == -1) { + printf("%s", prompt); + fflush(stdout); + if (fgets(buf, sizeof(buf), stdin) == NULL) { + return NULL; + } + count = strlen(buf); + if (count && buf[count-1] == '\n') { + count--; + buf[count] = '\0'; + } + } + else + { + current.buf = buf; + current.bufmax = sizeof(buf); + current.len = 0; + current.chars = 0; + current.pos = 0; + current.prompt = prompt; + + count = linenoisePrompt(¤t); + disableRawMode(¤t); + printf("\n"); + if (count == -1) { + return NULL; + } + } + return strdup(buf); +} + +/* Using a circular buffer is smarter, but a bit more complex to handle. */ +int linenoiseHistoryAdd(const char *line) { + char *linecopy; + + if (history_max_len == 0) return 0; + if (history == NULL) { + history = (char**)malloc(sizeof(char*)*history_max_len); + if (history == NULL) return 0; + memset(history,0,(sizeof(char*)*history_max_len)); + } + linecopy = strdup(line); + if (!linecopy) return 0; + if (history_len == history_max_len) { + free(history[0]); + memmove(history,history+1,sizeof(char*)*(history_max_len-1)); + history_len--; + } + history[history_len] = linecopy; + history_len++; + return 1; +} + +int linenoiseHistorySetMaxLen(int len) { + char **newHistory; + + if (len < 1) return 0; + if (history) { + int tocopy = history_len; + + newHistory = (char**)malloc(sizeof(char*)*len); + if (newHistory == NULL) return 0; + if (len < tocopy) tocopy = len; + memcpy(newHistory,history+(history_max_len-tocopy), sizeof(char*)*tocopy); + free(history); + history = newHistory; + } + history_max_len = len; + if (history_len > history_max_len) + history_len = history_max_len; + return 1; +} + +/* Save the history in the specified file. On success 0 is returned + * otherwise -1 is returned. */ +int linenoiseHistorySave(const char *filename) { + FILE *fp = fopen(filename,"w"); + int j; + + if (fp == NULL) return -1; + for (j = 0; j < history_len; j++) { + const char *str = history[j]; + /* Need to encode backslash, nl and cr */ + while (*str) { + if (*str == '\\') { + fputs("\\\\", fp); + } + else if (*str == '\n') { + fputs("\\n", fp); + } + else if (*str == '\r') { + fputs("\\r", fp); + } + else { + fputc(*str, fp); + } + str++; + } + fputc('\n', fp); + } + + fclose(fp); + return 0; +} + +/* Load the history from the specified file. If the file does not exist + * zero is returned and no operation is performed. + * + * If the file exists and the operation succeeded 0 is returned, otherwise + * on error -1 is returned. */ +int linenoiseHistoryLoad(const char *filename) { + FILE *fp = fopen(filename,"r"); + char buf[LINENOISE_MAX_LINE]; + + if (fp == NULL) return -1; + + while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) { + char *src, *dest; + + /* Decode backslash escaped values */ + for (src = dest = buf; *src; src++) { + char ch = *src; + + if (ch == '\\') { + src++; + if (*src == 'n') { + ch = '\n'; + } + else if (*src == 'r') { + ch = '\r'; + } else { + ch = *src; + } + } + *dest++ = ch; + } + /* Remove trailing newline */ + if (dest != buf && (dest[-1] == '\n' || dest[-1] == '\r')) { + dest--; + } + *dest = 0; + + linenoiseHistoryAdd(buf); + } + fclose(fp); + return 0; +} + +/* Provide access to the history buffer. + * + * If 'len' is not NULL, the length is stored in *len. + */ +char **linenoiseHistory(int *len) { + if (len) { + *len = history_len; + } + return history; +} diff --git a/release/src/router/usbmodeswitch/jim/linenoise.h b/release/src/router/usbmodeswitch/jim/linenoise.h new file mode 100644 index 0000000000..dcf22f7b96 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/linenoise.h @@ -0,0 +1,62 @@ +/* linenoise.h -- guerrilla line editing library against the idea that a + * line editing lib needs to be 20,000 lines of C code. + * + * See linenoise.c for more information. + * + * ------------------------------------------------------------------------ + * + * Copyright (c) 2010, Salvatore Sanfilippo + * Copyright (c) 2010, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __LINENOISE_H +#define __LINENOISE_H + +/* Currently never enable completion */ +#define NO_COMPLETION + +#ifndef NO_COMPLETION +typedef struct linenoiseCompletions { + size_t len; + char **cvec; +} linenoiseCompletions; + +typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); +void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); +void linenoiseAddCompletion(linenoiseCompletions *, const char *); +#endif + +char *linenoise(const char *prompt); +int linenoiseHistoryAdd(const char *line); +int linenoiseHistorySetMaxLen(int len); +int linenoiseHistorySave(const char *filename); +int linenoiseHistoryLoad(const char *filename); +void linenoiseHistoryFree(void); +char **linenoiseHistory(int *len); + +#endif /* __LINENOISE_H */ diff --git a/release/src/router/usbmodeswitch/jim/make-bootstrap-jim b/release/src/router/usbmodeswitch/jim/make-bootstrap-jim new file mode 100755 index 0000000000..cc2cd5d932 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/make-bootstrap-jim @@ -0,0 +1,104 @@ +#!/bin/sh + +# This script writes to stdout, a single source file (e.g. jimsh0.c) +# which can be compiled to provide a bootstrap version of jimsh. +# e.g. cc -o jimsh0 jimsh0.c + +makeext() +{ + source="$1" + basename=`basename "$source" .tcl` +cat < +#else +#define TCL_PLATFORM_OS "unknown" +#define TCL_PLATFORM_PLATFORM "unix" +#define TCL_PLATFORM_PATH_SEPARATOR ":" +#define HAVE_VFORK +#define HAVE_WAITPID +#endif +EOF + +outputsource() +{ + sed -e '/#include.*jim/d' -e '/#include.*utf8/d' \ + -e '/^#.*if.*JIM_BOOTSTRAP/,/^#endif.*JIM_BOOTSTRAP/d' \ + -e 's/\/\*.*\*\///' -e '/^[ ]*\/\*/,/\*\//d' $1 +} + +# Now output header files, removing references to jim header files +for i in utf8.h jim.h jim-subcmd.h jimregexp.h ; do + outputsource $i +done + +# Now extension source code +for i in $tclexts; do + makeext $i.tcl +done +for i in $cexts; do + outputsource jim-$i.c +done +makeloadexts $allexts + +# And finally the core source code +for i in jim.c jim-subcmd.c utf8.c jim-interactive.c jim-format.c jimregexp.c jimsh.c; do + outputsource $i +done diff --git a/release/src/router/usbmodeswitch/jim/make-c-ext.tcl b/release/src/router/usbmodeswitch/jim/make-c-ext.tcl new file mode 100644 index 0000000000..fd1a056bad --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/make-c-ext.tcl @@ -0,0 +1,38 @@ +#!/usr/bin/env tclsh + +# Usage: make-c-ext.tcl source.tcl >jim-source.c + +# Converts a Tcl source file into C source suitable +# for loading as a static extension. + +lassign $argv source + +if {![string match *.tcl $source]} { + error "Source $source is not a .tcl file" +} + +# Read the Tcl source and convert to C +# Note that no lines are removed in order to preserve line numbering +set sourcelines {} +set f [open $source] +while {[gets $f buf] >= 0} { + # Remove comment lines + regsub {^[ \t]*#.*$} $buf "" buf + # Escape quotes and backlashes + set buf [string map [list \\ \\\\ \" \\"] $buf] + lappend sourcelines \"$buf\\n\" +} +close $f + +lappend lines {/* autogenerated - do not edit */} +lappend lines {#include } +set basename [file tail $source] +set pkgname [file rootname $basename] + +lappend lines "int Jim_${pkgname}Init(Jim_Interp *interp)" +lappend lines "\{" +lappend lines "\tif (Jim_PackageProvide(interp, \"$pkgname\", \"1.0\", JIM_ERRMSG)) return JIM_ERR;" +lappend lines "\treturn Jim_EvalSource(interp, \"$basename\", 1, [join $sourcelines \n]);" +lappend lines "\}" + +puts [join $lines \n] diff --git a/release/src/router/usbmodeswitch/jim/make-index b/release/src/router/usbmodeswitch/jim/make-index new file mode 100755 index 0000000000..8dba9202fd --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/make-index @@ -0,0 +1,70 @@ +#!/usr/bin/env tclsh +# vim:se syn=tcl: + +set filename [lindex $argv 0] +set f [open $filename] + +# Read the file looking for command definitions +set lines {} +set commands {} +array set cdict {} +set c 0 + +while {[gets $f buf] >= 0} { + if {[string match "~~*" $buf]} { + if {[string match "*:*" $prev]} { + incr c + set target cmd_$c + set lines [linsert $lines end-1 "\[\[$target\]\]"] + } else { + set target _$prev + } + foreach cmd [split $prev ":,"] { + set cmd [string trim $cmd] + if {[regexp {^[a-z.]+$} $cmd]} { + lappend commands [list $cmd $target] + set cdict($cmd) $target + } + } + } + lappend lines $buf + set prev $buf +} +close $f + +# Build the command index in the list: $index +lappend index {[frame="none",grid="none"]} +lappend index {|=========================} +set i 0 +set row {} +foreach command [lsort $commands] { + lassign $command cmd target + + append row "|<<$target,*`$cmd`*>> " + incr i + if {$i % 8 == 0} { + lappend index $row + set row {} + } +} +while {$i % 8 != 0} { + incr i + append row "| " +} +lappend index $row +lappend index {|=========================} + +# Map all `cmd` to <<$target,`cmd`>> +set mapping {} +foreach c [array names cdict] { + lappend mapping `$c` <<$cdict($c),*`$c`*>> + lappend mapping "`$c " "<<$cdict($c),*`$c`*>> `" +} + +# And the command index +lappend mapping @INSERTINDEX@ [join $index \n] + +# Output the result +foreach line $lines { + puts [string map $mapping $line] +} diff --git a/release/src/router/usbmodeswitch/jim/make-load-static-exts.tcl b/release/src/router/usbmodeswitch/jim/make-load-static-exts.tcl new file mode 100644 index 0000000000..9185e7756f --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/make-load-static-exts.tcl @@ -0,0 +1,48 @@ +#!/usr/bin/env tclsh + +# Usage: make-load-static-exts extname ... >load-static-exts.c + +# Creates load-static-exts.c based on the configured static extensions + +# There are some dependencies on static extensions which require +# a certain load order. Do this by setting priorities and sorting. + +array set pri { + stdlib 0 + readdir 1 + glob 2 + oo 1 + tree 2 + pack 1 + binary 2 +} + +foreach i $argv { + set p 1 + if {[info exists pri($i)]} { + set p $pri($i) + } + lappend exts [list $p $i] +} +set exts [lsort $exts] + +puts { +/* autogenerated - do not edit */ +#include "jim.h" +#include "jimautoconf.h" +int Jim_InitStaticExtensions(Jim_Interp *interp) +} +puts \{ + +foreach e $exts { + set ext [lindex $e 1] + puts "\textern int Jim_${ext}Init(Jim_Interp *);" +} +foreach e $exts { + set ext [lindex $e 1] + puts "\tJim_${ext}Init(interp);" +} + +puts "\treturn JIM_OK;" + +puts \} diff --git a/release/src/router/usbmodeswitch/jim/parse-unidata.tcl b/release/src/router/usbmodeswitch/jim/parse-unidata.tcl new file mode 100644 index 0000000000..4b5ec3aae4 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/parse-unidata.tcl @@ -0,0 +1,64 @@ +#!/usr/bin/env tclsh + +# Generate UTF-8 case mapping tables +# +# (c) 2010 Steve Bennett +# +# See LICENCE for licence details. +#/ + +# Parse the unicode data from: http://unicode.org/Public/UNIDATA/UnicodeData.txt +# to generate case mapping tables + +set f [open [lindex $argv 0]] +set extoff 0 +puts "static const struct casemap unicode_case_mapping\[\] = \{" +while {[gets $f buf] >= 0} { + foreach {code name class x x x x x x x x x upper lower} [split $buf ";"] break + set code 0x$code + if {$code <= 0x7f} { + continue + } + if {$code > 0xffff} { + break + } + if {$class ne "Lu" && $class ne "Ll"} { + continue + } + if {$upper eq ""} { + set upper $code + } else { + set upper 0x$upper + } + if {$lower eq ""} { + set lower $code + } else { + set lower 0x$lower + } + if {$upper == $code && $lower == $code} { + continue + } + set l [expr {$lower - $code}] + set u [expr {$upper - $code}] + if {abs($u) > 127 || abs($l) > 127} { + # Can't encode both in one byte, so use indirection + lappend jumptable $code $lower $upper + set l -128 + set u $extoff + incr extoff + if {$extoff > 0xff} { + error "Too many entries in the offset table!" + } + } + set entry [string tolower "$code, $l, $u"] + puts " { $entry }," +} +close $f +puts "\};\n" + +# Now the jump table +puts "static const struct caseextmap unicode_extmap\[\] = \{" +foreach {c l u} $jumptable { + puts " { $l, $u }," +} +puts "\};\n" diff --git a/release/src/router/usbmodeswitch/jim/regtest.tcl b/release/src/router/usbmodeswitch/jim/regtest.tcl new file mode 100644 index 0000000000..de796d9e97 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/regtest.tcl @@ -0,0 +1,158 @@ +# REGTEST 1 +# 27Jan2005 - SIGSEGV for bug on Jim_DuplicateObj(). + +for {set i 0} {$i < 100} {incr i} { + set a "x" + lappend a n +} +puts "TEST 1 PASSED" + +# REGTEST 2 +# 29Jan2005 - SEGFAULT parsing script composed of just one comment. +eval {#foobar} +puts "TEST 2 PASSED" + +# REGTEST 3 +# 29Jan2005 - "Error in Expression" with correct expression +set x 5 +expr {$x-5} +puts "TEST 3 PASSED" + +# REGTEST 4 +# 29Jan2005 - SIGSEGV when run this code, due to expr's bug. +proc fibonacci {x} { + if {$x <= 1} { + expr 1 + } else { + expr {[fibonacci [expr {$x-1}]] + [fibonacci [expr {$x-2}]]} + } +} +fibonacci 6 +puts "TEST 4 PASSED" + +# REGTEST 5 +# 06Mar2005 - This looped forever... +for {set i 0} {$i < 10} {incr i} {continue} +puts "TEST 5 PASSED" + +# REGTEST 6 +# 07Mar2005 - Unset create variable + dict is using dict syntax sugar at +# currently non-existing variable +catch {unset thisvardoesnotexists(thiskeytoo)} +if {[catch {set thisvardoesnotexists}] == 0} { + puts "TEST 6 FAILED - unset created dict for non-existing variable" + break +} +puts "TEST 6 PASSED" + +# REGTEST 7 +# 04Nov2008 - variable parsing does not eat last brace +set a 1 +list ${a} +puts "TEST 7 PASSED" + +# REGTEST 8 +# 04Nov2008 - string toupper/tolower do not convert to string rep +string tolower [list a] +string toupper [list a] +puts "TEST 8 PASSED" + +# REGTEST 9 +# 04Nov2008 - crash on exit when replacing Tcl proc with C command. +# Requires the clock extension to be built as a loadable module. +proc clock {args} {} +catch {package require clock} +# Note, crash on exit, so don't say we passed! + +# REGTEST 10 +# 05Nov2008 - incorrect lazy expression evaluation with unary not +expr {1 || !0} +puts "TEST 10 PASSED" + +# REGTEST 11 +# 14 Feb 2010 - access static variable in deleted proc +proc a {} {{x 1}} { rename a ""; incr x } +a +puts "TEST 11 PASSED" + +# REGTEST 12 +# 13 Sep 2010 - reference with invalid tag +set a b[ref value "tag name"] +getref [string range $a 1 end] +puts "TEST 12 PASSED" + +# REGTEST 13 +# 14 Sep 2010 - parse list with trailing backslash +set x "switch -0 \$on \\" +lindex $x 1 +puts "TEST 13 PASSED" + +# REGTEST 14 +# 14 Sep 2010 - command expands to nothing +eval "{*}{}" +puts "TEST 14 PASSED" + +# REGTEST 15 +# 24 Feb 2010 - bad reference counting of the stack trace in 'error' +proc a {msg stack} { + tailcall error $msg $stack +} +catch {fail} msg opts +catch {a $msg $opts(-errorinfo)} + +# REGTEST 16 +# 24 Feb 2010 - rename the current proc +# Leaves unfreed objects on the stack +proc a {} { rename a newa} +a + +# REGTEST 17 +# 26 Nov 2010 - crashes on invalid dict sugar +catch {eval {$x(}} +puts "TEST 17 PASSED" + +# REGTEST 18 +# 12 Apr 2011 - crashes on unset for loop var +catch { + for {set i 0} {$i < 5} {incr i} {unset i} +} +puts "TEST 18 PASSED" + +# REGTEST 19 +# 25 May 2011 - crashes with double colon +catch { + expr {5 ne ::} +} +puts "TEST 19 PASSED" + +# REGTEST 20 +# 26 May 2011 - infinite recursion +proc a {} { global ::blah; set ::blah test } +a +puts "TEST 20 PASSED" + +# REGTEST 21 +# 26 May 2011 - infinite loop with null byte in subst +subst "abc\0def" +puts "TEST 21 PASSED" + +# REGTEST 22 +# 21 June 2011 - crashes on lappend to to value with script rep +set x rand +eval $x +lappend x b +puts "TEST 22 PASSED" + +# REGTEST 23 +# 27 July 2011 - unfreed objects on exit +catch { + set x abc + subst $x + regexp $x $x +} +# Actually, the test passes if no objects leaked on exit +puts "TEST 23 PASSED" + +# TAKE THE FOLLOWING puts AS LAST LINE + +puts "--- ALL TESTS PASSED ---" diff --git a/release/src/router/usbmodeswitch/jim/rlprompt.tcl b/release/src/router/usbmodeswitch/jim/rlprompt.tcl new file mode 100644 index 0000000000..cdd78d4cfe --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/rlprompt.tcl @@ -0,0 +1,45 @@ +# Readline-based interactive shell for Jim +# Copyright(C) 2005 Salvatore Sanfilippo +# +# In order to automatically have readline-editing features +# put this in your $HOME/.jimrc +# +# if {$jim_interactive} { +# if {[catch {package require rlprompt}] == 0} { +# rlprompt.shell +# } +# } +package require readline + +proc rlprompt.shell {} { + puts "Readline shell loaded" + puts "Welcome to Jim [info version]!" + set prompt ". " + set buf "" + while 1 { + set line [readline.readline $prompt] + + if {[string length $line] == 0} { + continue + } + if {$buf eq ""} { + set buf $line + } else { + append buf \n $line + } + if {![info complete $buf]} { + set prompt "> " + continue + } + readline.addhistory $buf + + catch { + uplevel #0 $buf + } error + if {$error ne ""} { + puts $error + } + set buf "" + set prompt ". " + } +} diff --git a/release/src/router/usbmodeswitch/jim/stdlib.tcl b/release/src/router/usbmodeswitch/jim/stdlib.tcl new file mode 100644 index 0000000000..3abeb3e9fe --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/stdlib.tcl @@ -0,0 +1,150 @@ +# Create a single word alias (proc) for one or more words +# e.g. alias x info exists +# if {[x var]} ... +proc alias {name args} { + set prefix $args + proc $name args prefix { + tailcall {*}$prefix {*}$args + } +} + +# Creates an anonymous procedure +proc lambda {arglist args} { + set name [ref {} function lambda.finalizer] + tailcall proc $name $arglist {*}$args +} + +proc lambda.finalizer {name val} { + rename $name {} +} + +# Like alias, but creates and returns an anonyous procedure +proc curry {args} { + set prefix $args + lambda args prefix { + tailcall {*}$prefix {*}$args + } +} + +# Returns the given argument. +# Useful with 'local' as follows: +# proc a {} {...} +# local function a +# +# set x [lambda ...] +# local function $x +# +proc function {value} { + return $value +} + +# Tcl 8.5 lassign +proc lassign {list args} { + # in case the list is empty... + lappend list {} + uplevel 1 [list foreach $args $list break] + lrange $list [llength $args] end-1 +} + +# Returns a list of proc filename line ... +# with 3 entries for each stack frame (proc), +# (deepest level first) +proc stacktrace {} { + set trace {} + foreach level [range 1 [info level]] { + lassign [info frame -$level] p f l + lappend trace $p $f $l + } + return $trace +} + +# Returns a human-readable version of a stack trace +proc stackdump {stacktrace} { + set result {} + set count 0 + foreach {l f p} [lreverse $stacktrace] { + if {$count} { + append result \n + } + incr count + if {$p ne ""} { + append result "in procedure '$p' " + if {$f ne ""} { + append result "called " + } + } + if {$f ne ""} { + append result "at file \"$f\", line $l" + } + } + return $result +} + +# Sort of replacement for $::errorInfo +# Usage: errorInfo error ?stacktrace? +proc errorInfo {msg {stacktrace ""}} { + if {$stacktrace eq ""} { + set stacktrace [info stacktrace] + } + lassign $stacktrace p f l + if {$f ne ""} { + set result "Runtime Error: $f:$l: " + } + append result "$msg\n" + append result [stackdump $stacktrace] + + # Remove the trailing newline + string trim $result +} + +# Finds the current executable by searching along the path +# Returns the empty string if not found. +proc {info nameofexecutable} {} { + if {[info exists ::jim_argv0]} { + if {[string match "*/*" $::jim_argv0]} { + return [file join [pwd] $::jim_argv0] + } + foreach path [split [env PATH ""] $::tcl_platform(pathSeparator)] { + set exec [file join [pwd] $path $::jim_argv0] + if {[file executable $exec]} { + return $exec + } + } + } + return "" +} + +# Script-based implementation of 'dict with' +proc {dict with} {dictVar args script} { + upvar $dictVar dict + set keys {} + foreach {n v} [dict get $dict {*}$args] { + upvar $n var_$n + set var_$n $v + lappend keys $n + } + catch {uplevel 1 $script} msg opts + if {[info exists dict] && [dict exists $dict {*}$args]} { + foreach n $keys { + if {[info exists var_$n]} { + dict set dict {*}$args $n [set var_$n] + } else { + dict unset dict {*}$args $n + } + } + } + return {*}$opts $msg +} + +# Script-based implementation of 'dict merge' +# This won't get called in the trivial case of no args +proc {dict merge} {dict args} { + foreach d $args { + # Check for a valid dict + dict size $d + foreach {k v} $d { + dict set dict $k $v + } + } + return $dict +} diff --git a/release/src/router/usbmodeswitch/jim/tcl.license.terms b/release/src/router/usbmodeswitch/jim/tcl.license.terms new file mode 100644 index 0000000000..f1dcaa5245 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/tcl.license.terms @@ -0,0 +1,40 @@ +This software is copyrighted by the Regents of the University of +California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState +Corporation and other parties. The following terms apply to all files +associated with the software unless explicitly disclaimed in +individual files. + +The authors hereby grant permission to use, copy, modify, distribute, +and license this software and its documentation for any purpose, provided +that existing copyright notices are retained in all copies and that this +notice is included verbatim in any distributions. No written agreement, +license, or royalty fee is required for any of the authorized uses. +Modifications to this software may be copyrighted by their authors +and need not follow the licensing terms described here, provided that +the new terms are clearly indicated on the first page of each file where +they apply. + +IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +MODIFICATIONS. + +GOVERNMENT USE: If you are acquiring this software on behalf of the +U.S. government, the Government shall have only "Restricted Rights" +in the software and related documentation as defined in the Federal +Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you +are acquiring the software on behalf of the Department of Defense, the +software shall be classified as "Commercial Computer Software" and the +Government shall have only "Restricted Rights" as defined in Clause +252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the +authors grant the U.S. Government and others acting in its behalf +permission to use and distribute the software in accordance with the +terms specified in this license. diff --git a/release/src/router/usbmodeswitch/jim/tclcompat.tcl b/release/src/router/usbmodeswitch/jim/tclcompat.tcl new file mode 100644 index 0000000000..8e5b128eb0 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/tclcompat.tcl @@ -0,0 +1,279 @@ +# (c) 2008 Steve Bennett +# +# Loads some Tcl-compatible features. +# I/O commands, case, lassign, parray, errorInfo, ::tcl_platform, ::env +# try, throw, file copy, file delete -force + +# Set up the ::env array +set env [env] + +if {[info commands stdout] ne ""} { + # Tcl-compatible I/O commands + foreach p {gets flush close eof seek tell} { + proc $p {chan args} {p} { + tailcall $chan $p {*}$args + } + } + unset p + + # puts is complicated by -nonewline + # + proc puts {{-nonewline {}} {chan stdout} msg} { + if {${-nonewline} ni {-nonewline {}}} { + tailcall ${-nonewline} puts $msg + } + tailcall $chan puts {*}${-nonewline} $msg + } + + # read is complicated by -nonewline + # + # read chan ?maxchars? + # read -nonewline chan + proc read {{-nonewline {}} chan} { + if {${-nonewline} ni {-nonewline {}}} { + tailcall ${-nonewline} read {*}${chan} + } + tailcall $chan read {*}${-nonewline} + } + + proc fconfigure {f args} { + foreach {n v} $args { + switch -glob -- $n { + -bl* { + $f ndelay $v + } + -bu* { + $f buffering $v + } + -tr* { + # Just ignore -translation + } + default { + return -code error "fconfigure: unknown option $n" + } + } + } + } +} + +# case var ?in? pattern action ?pattern action ...? +proc case {var args} { + # Skip dummy parameter + if {[lindex $args 0] eq "in"} { + set args [lrange $args 1 end] + } + + # Check for single arg form + if {[llength $args] == 1} { + set args [lindex $args 0] + } + + # Check for odd number of args + if {[llength $args] % 2 != 0} { + return -code error "extra case pattern with no body" + } + + # Internal function to match a value agains a list of patterns + local proc case.checker {value pattern} { + string match $pattern $value + } + + foreach {value action} $args { + if {$value eq "default"} { + set do_action $action + continue + } elseif {[lsearch -bool -command case.checker $value $var]} { + set do_action $action + break + } + } + + if {[info exists do_action]} { + set rc [catch [list uplevel 1 $do_action] result opts] + if {$rc} { + incr opts(-level) + } + return {*}$opts $result + } +} + +# fileevent isn't needed in Jim, but provide it for compatibility +proc fileevent {args} { + tailcall {*}$args +} + +# Second, option argument is a glob pattern +# Third, optional argument is a "putter" function +# +proc parray {arrayname {pattern *} {puts puts}} { + upvar $arrayname a + + set max 0 + foreach name [array names a $pattern]] { + if {[string length $name] > $max} { + set max [string length $name] + } + } + incr max [string length $arrayname] + incr max 2 + foreach name [lsort [array names a $pattern]] { + $puts [format "%-${max}s = %s" $arrayname\($name\) $a($name)] + } +} + +# Implements 'file copy' - single file mode only +proc {file copy} {{force {}} source target} { + try { + if {$force ni {{} -force}} { + error "bad option \"$force\": should be -force" + } + + set in [open $source] + + if {$force eq "" && [file exists $target]} { + $in close + error "error copying \"$source\" to \"$target\": file already exists" + } + set out [open $target w] + $in copyto $out + $out close + } on error {msg opts} { + incr opts(-level) + return {*}$opts $msg + } finally { + catch {$in close} + } +} + +# 'open "|..." ?mode?" will invoke this wrapper around exec/pipe +# Note that we return a lambda which also provides the 'pid' command +proc popen {cmd {mode r}} { + lassign [socket pipe] r w + try { + if {[string match "w*" $mode]} { + lappend cmd <@$r & + set pids [exec {*}$cmd] + $r close + set f $w + } else { + lappend cmd >@$w & + set pids [exec {*}$cmd] + $w close + set f $r + } + lambda {cmd args} {f pids} { + if {$cmd eq "pid"} { + return $pids + } + if {$cmd eq "close"} { + $f close + # And wait for the child processes to complete + foreach p $pids { os.wait $p } + return + } + tailcall $f $cmd {*}$args + } + } on error {error opts} { + $r close + $w close + error $error + } +} + +# A wrapper around 'pid' which can return the pids for 'popen' +local proc pid {{chan {}}} { + if {$chan eq ""} { + tailcall upcall pid + } + if {[catch {$chan tell}]} { + return -code error "can not find channel named \"$chan\"" + } + if {[catch {$chan pid} pids]} { + return "" + } + return $pids +} + +# try/on/finally conceptually similar to Tcl 8.6 +# +# Usage: try ?catchopts? script ?onclause ...? ?finallyclause? +# +# Where: +# onclause is: on codes {?resultvar? ?optsvar?} script +# +# codes is: a list of return codes (ok, error, etc. or integers), or * for any +# +# finallyclause is: finally script +# +# +# Where onclause is: on codes {?resultvar? ?optsvar?} +proc try {args} { + set catchopts {} + while {[string match -* [lindex $args 0]]} { + set args [lassign $args opt] + if {$opt eq "--"} { + break + } + lappend catchopts $opt + } + if {[llength $args] == 0} { + return -code error {wrong # args: should be "try ?options? script ?argument ...?"} + } + set args [lassign $args script] + set code [catch -eval {*}$catchopts [list uplevel 1 $script] msg opts] + + set handled 0 + + foreach {on codes vars script} $args { + switch -- $on \ + on { + if {!$handled && ($codes eq "*" || [info returncode $code] in $codes)} { + lassign $vars msgvar optsvar + if {$msgvar ne ""} { + upvar $msgvar hmsg + set hmsg $msg + } + if {$optsvar ne ""} { + upvar $optsvar hopts + set hopts $opts + } + # Override any body result + set code [catch [list uplevel 1 $script] msg opts] + incr handled + } + } \ + finally { + set finalcode [catch [list uplevel 1 $codes] finalmsg finalopts] + if {$finalcode} { + # Override any body or handler result + set code $finalcode + set msg $finalmsg + set opts $finalopts + } + break + } \ + default { + return -code error "try: expected 'on' or 'finally', got '$on'" + } + } + + if {$code} { + incr opts(-level) + return {*}$opts $msg + } + return $msg +} + +# Generates an exception with the given code (ok, error, etc. or an integer) +# and the given message +proc throw {code {msg ""}} { + return -code $code $msg +} + +# Helper for "file delete -force" +proc {file delete force} {path} { + foreach e [readdir $path] { + file delete -force $path/$e + } + file delete $path +} diff --git a/release/src/router/usbmodeswitch/jim/tree.tcl b/release/src/router/usbmodeswitch/jim/tree.tcl new file mode 100644 index 0000000000..01fc16704e --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/tree.tcl @@ -0,0 +1,219 @@ +# Conceptually compatible with tcllib ::struct::tree +# but uses an object based interface. +# To mimic tcllib, do: +# rename [tree] mytree + +package require oo + +# set pt [tree] +# +# Create a tree +# This automatically creates a node named "root" +# +# $pt destroy +# +# Destroy the tree and all it's nodes +# +# $pt set +# +# Set the value for the given key +# +# $pt lappend ... +# +# Append to the (list) value(s) for the given key, or set if not yet set +# +# $pt keyexists +# +# Returns 1 if the given key exists +# +# $pt get +# +# Returns the value associated with the given key +# +# $pt getall +# +# Returns the entire attribute dictionary associated with the given key +# +# $pt depth +# +# Returns the depth of the given node. The depth of "root" is 0. +# +# $pt parent +# +# Returns the name of the parent node, or "" for the root node. +# +# $pt numchildren +# +# Returns the number of child nodes. +# +# $pt children +# +# Returns a list of the child nodes. +# +# $pt next +# +# Returns the next sibling node, or "" if none. +# +# $pt insert ?index? +# +# Add a new child node to the given node. +# THe default index is "end" +# Returns the name of the newly added node +# +# $pt walk dfs|bfs {actionvar nodevar} +# +# Walks the tree starting from the given node, either breadth first (bfs) +# depth first (dfs). +# The value "enter" or "exit" is stored in variable $actionvar +# The name of each node is stored in $nodevar. +# The script $code is evaluated twice for each node, on entry and exit. +# +# $pt dump +# +# Dumps the tree contents to stdout + +#------------------------------------------ +# Internal implementation. +# The tree class has 4 instance variables. +# - tree is a dictionary. key=node, value=node value dictionary +# - parent is a dictionary. key=node, value=parent of this node +# - children is a dictionary. key=node, value=list of child nodes for this node +# - nodeid is an integer which increments to give each node a unique id + +# Construct a tree with a single root node with no parent and no children +class tree { + tree {root {}} + parents {root {}} + children {root {}} + nodeid 0 +} + +# Simply walk up the tree to get the depth +tree method depth {node} { + set depth 0 + while {$node ne "root"} { + incr depth + set node [dict get $parents $node] + } + return $depth +} + +tree method parent {node} { + dict get $parents $node +} + +tree method children {node} { + dict get $children $node +} + +tree method numchildren {node} { + llength [dict get $children $node] +} + +tree method next {node} { + # My siblings are my parents children + set siblings [dict get $children [dict get $parents $node]] + # Find me + set i [lsearch $siblings $node] + incr i + lindex $siblings $i +} + +tree method set {node key value} { + dict set tree $node $key $value + return $value +} + +tree method get {node key} { + dict get $tree $node $key +} + +tree method keyexists {node key} { + dict exists $tree $node $key +} + +tree method getall {node} { + dict get $tree $node +} + +tree method insert {node {index end}} { + + # Make a new node and add it to the tree + set childname node[incr nodeid] + dict set tree $childname {} + + # The new node has no children + dict set children $childname {} + + # Set the parent + dict set parents $childname $node + + # And add it as a child + set nodes [dict get $children $node] + dict set children $node [linsert $nodes $index $childname] + + return $childname +} + +tree method lappend {node key args} { + if {[dict exists $tree $node $key]} { + set result [dict get $tree $node $key] + } + lappend result {*}$args + dict set tree $node $key $result + return $result +} + +# $tree walk node bfs|dfs {action loopvar} +# +tree method walk {node type vars code} { + # set up vars + lassign $vars actionvar namevar + + set n $node + + if {$type ne "child"} { + upvar 2 $namevar name $actionvar action + + # Enter this node + set name $node + set action enter + + uplevel 2 $code + } + + if {$type eq "dfs"} { + # Depth-first so do the children + foreach child [$self children $n] { + uplevel 2 [list $self walk $child $type $vars $code] + } + } elseif {$type ne "none"} { + # Breadth-first so do the children to one level only + foreach child [$self children $n] { + uplevel 2 [list $self walk $child none $vars $code] + } + + # Now our grandchildren + foreach child [$self children $n] { + uplevel 2 [list $self walk $child child $vars $code] + } + } + + if {$type ne "child"} { + # Exit this node + set name $node + set action exit + + uplevel 2 $code + } +} + +tree method dump {} { + $self walk root dfs {action n} { + set indent [string repeat " " [$self depth $n]] + if {$action eq "enter"} { + puts "$indent$n ([$self getall $n])" + } + } + puts "" +} diff --git a/release/src/router/usbmodeswitch/jim/utf8.c b/release/src/router/usbmodeswitch/jim/utf8.c new file mode 100644 index 0000000000..a81b3deef0 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/utf8.c @@ -0,0 +1,192 @@ +/** + * UTF-8 utility functions + * + * (c) 2010 Steve Bennett + * + * See LICENCE for licence details. + */ + +#include +#include +#include +#include +#include +#include "utf8.h" + +/* This one is always implemented */ +int utf8_fromunicode(char *p, unsigned short uc) +{ + if (uc <= 0x7f) { + *p = uc; + return 1; + } + else if (uc <= 0x7ff) { + *p++ = 0xc0 | ((uc & 0x7c0) >> 6); + *p = 0x80 | (uc & 0x3f); + return 2; + } + else { + *p++ = 0xe0 | ((uc & 0xf000) >> 12); + *p++ = 0x80 | ((uc & 0xfc0) >> 6); + *p = 0x80 | (uc & 0x3f); + return 3; + } +} + +#if defined(JIM_UTF8) && !defined(JIM_BOOTSTRAP) +int utf8_charlen(int c) +{ + if ((c & 0x80) == 0) { + return 1; + } + if ((c & 0xe0) == 0xc0) { + return 2; + } + if ((c & 0xf0) == 0xe0) { + return 3; + } + if ((c & 0xf8) == 0xf0) { + return 4; + } + /* Invalid sequence */ + return -1; +} + +int utf8_strlen(const char *str, int bytelen) +{ + int charlen = 0; + if (bytelen < 0) { + bytelen = strlen(str); + } + while (bytelen) { + int c; + int l = utf8_tounicode(str, &c); + charlen++; + str += l; + bytelen -= l; + } + return charlen; +} + +int utf8_index(const char *str, int index) +{ + const char *s = str; + while (index--) { + int c; + s += utf8_tounicode(s, &c); + } + return s - str; +} + +int utf8_charequal(const char *s1, const char *s2) +{ + int c1, c2; + + utf8_tounicode(s1, &c1); + utf8_tounicode(s2, &c2); + + return c1 == c2; +} + +int utf8_prev_len(const char *str, int len) +{ + int n = 1; + + assert(len > 0); + + /* Look up to len chars backward for a start-of-char byte */ + while (--len) { + if ((str[-n] & 0x80) == 0) { + /* Start of a 1-byte char */ + break; + } + if ((str[-n] & 0xc0) == 0xc0) { + /* Start of a multi-byte char */ + break; + } + n++; + } + return n; +} + +int utf8_tounicode(const char *str, int *uc) +{ + unsigned const char *s = (unsigned const char *)str; + + if (s[0] < 0xc0) { + *uc = s[0]; + return 1; + } + if (s[0] < 0xe0) { + if ((s[1] & 0xc0) == 0x80) { + *uc = ((s[0] & ~0xc0) << 6) | (s[1] & ~0x80); + return 2; + } + } + else if (s[0] < 0xf0) { + if (((str[1] & 0xc0) == 0x80) && ((str[2] & 0xc0) == 0x80)) { + *uc = ((s[0] & ~0xe0) << 12) | ((s[1] & ~0x80) << 6) | (s[2] & ~0x80); + return 3; + } + } + + /* Invalid sequence, so just return the byte */ + *uc = *s; + return 1; +} + +struct casemap { + unsigned short code; /* code point */ + signed char lowerdelta; /* add for lowercase, or if -128 use the ext table */ + signed char upperdelta; /* add for uppercase, or offset into the ext table */ +}; + +/* Extended table for codepoints where |delta| > 127 */ +struct caseextmap { + unsigned short lower; + unsigned short upper; +}; + +/* Generated mapping tables */ +#include "_unicode_mapping.c" + +#define NUMCASEMAP sizeof(unicode_case_mapping) / sizeof(*unicode_case_mapping) + +static int cmp_casemap(const void *key, const void *cm) +{ + return *(int *)key - (int)((const struct casemap *)cm)->code; +} + +static int utf8_map_case(int uc, int upper) +{ + const struct casemap *cm = bsearch(&uc, unicode_case_mapping, NUMCASEMAP, sizeof(*unicode_case_mapping), cmp_casemap); + + if (cm) { + if (cm->lowerdelta == -128) { + uc = upper ? unicode_extmap[cm->upperdelta].upper : unicode_extmap[cm->upperdelta].lower; + } + else { + uc += upper ? cm->upperdelta : cm->lowerdelta; + } + } + return uc; +} + +int utf8_upper(int uc) +{ + if (isascii(uc)) { + return toupper(uc); + } + return utf8_map_case(uc, 1); +} + +int utf8_lower(int uc) +{ + if (isascii(uc)) { + return tolower(uc); + } + + return utf8_map_case(uc, 0); +} + +#endif /* JIM_BOOTSTRAP */ diff --git a/release/src/router/usbmodeswitch/jim/utf8.h b/release/src/router/usbmodeswitch/jim/utf8.h new file mode 100644 index 0000000000..9ada93fc61 --- /dev/null +++ b/release/src/router/usbmodeswitch/jim/utf8.h @@ -0,0 +1,110 @@ +#ifndef UTF8_UTIL_H +#define UTF8_UTIL_H +/** + * UTF-8 utility functions + * + * (c) 2010 Steve Bennett + * + * See LICENCE for licence details. + */ +#include + +/** + * Converts the given unicode codepoint (0 - 0xffff) to utf-8 + * and stores the result at 'p'. + * + * Returns the number of utf-8 characters (1-3). + */ +int utf8_fromunicode(char *p, unsigned short uc); + +#ifndef JIM_UTF8 +#include + +/* No utf-8 support. 1 byte = 1 char */ +#define utf8_strlen(S, B) ((B) < 0 ? strlen(S) : (B)) +#define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1) +#define utf8_upper(C) toupper(C) +#define utf8_lower(C) tolower(C) +#define utf8_index(C, I) (I) +#define utf8_charlen(C) 1 +#define utf8_prev_len(S, L) 1 + +#else +#if !defined(JIM_BOOTSTRAP) +/** + * Returns the length of the utf-8 sequence starting with 'c'. + * + * Returns 1-4, or -1 if this is not a valid start byte. + * + * Note that charlen=4 is not supported by the rest of the API. + */ +int utf8_charlen(int c); + +/** + * Returns the number of characters in the utf-8 + * string of the given byte length. + * + * Any bytes which are not part of an valid utf-8 + * sequence are treated as individual characters. + * + * The string *must* be null terminated. + * + * Does not support unicode code points > \uffff + */ +int utf8_strlen(const char *str, int bytelen); + +/** + * Returns the byte index of the given character in the utf-8 string. + * + * The string *must* be null terminated. + * + * This will return the byte length of a utf-8 string + * if given the char length. + */ +int utf8_index(const char *str, int charindex); + +/** + * Returns the unicode codepoint corresponding to the + * utf-8 sequence 'str'. + * + * Stores the result in *uc and returns the number of bytes + * consumed. + * + * If 'str' is null terminated, then an invalid utf-8 sequence + * at the end of the string will be returned as individual bytes. + * + * If it is not null terminated, the length *must* be checked first. + * + * Does not support unicode code points > \uffff + */ +int utf8_tounicode(const char *str, int *uc); + +/** + * Returns the number of bytes before 'str' that the previous + * utf-8 character sequence starts (which may be the middle of a sequence). + * + * Looks back at most 'len' bytes backwards, which must be > 0. + * If no start char is found, returns -len + */ +int utf8_prev_len(const char *str, int len); + +/** + * Returns the upper-case variant of the given unicode codepoint. + * + * Does not support unicode code points > \uffff + */ +int utf8_upper(int uc); + +/** + * Returns the lower-case variant of the given unicode codepoint. + * + * NOTE: Use utf8_upper() in preference for case-insensitive matching. + * + * Does not support unicode code points > \uffff + */ +int utf8_lower(int uc); +#endif /* JIM_BOOTSTRAP */ + +#endif + +#endif diff --git a/release/src/router/usbmodeswitch/make_string.tcl b/release/src/router/usbmodeswitch/make_string.tcl new file mode 100755 index 0000000000..5a1b5ff18b --- /dev/null +++ b/release/src/router/usbmodeswitch/make_string.tcl @@ -0,0 +1,31 @@ +#!/usr/bin/env tclsh + +# (c) Josua Dietze 2012 +# +# Usage: make_string.tcl source.tcl >jim-source.c + +# Converts a Tcl source file into C source suitable +# for using as an embedded script. + +set source [lindex $argv 0] + +if {![string match *.tcl $source]} { + error "Source $source is not a .tcl file" +} + +# Read the Tcl source and convert to C macro +set sourcelines {} +set f [open $source] +while {[gets $f buf] >= 0} { + # Remove comment lines + regsub {^[ \t]*#.*$} $buf "" buf + # Remove leading whitespaces + set buf [string trimleft $buf] + # Escape quotes and backlashes + set buf [string map [list \\ \\\\ \" \\"] $buf] + if [string length $buf] { + lappend sourcelines "$buf\\n" + } +} +close $f +puts "#define RAW \"[join $sourcelines ""]\"" diff --git a/release/src/router/usbmodeswitch/usb_modeswitch b/release/src/router/usbmodeswitch/usb_modeswitch new file mode 100755 index 0000000000000000000000000000000000000000..40b2f1ee51ca2dc21926d760a65da5a0940d8eb3 GIT binary patch literal 61213 zcwWuD4|p8Kl`mQvjcr-jFc@J(5YRGXTM${uMkKRXy`qMpMT;I4MFE5X&%f$*9K_ewTR)u@- z-!itq!3zJ|1`mgn78R5%MW5kAjbfBEUs5O_mgIz(WJlC=?^2 zSf*<@xO_Z}A7(tv<8gUBUBuOhfP}#t80U-9UHIRcN5RL#S-=z)NcwmDDivf|E|osl zMd>$2!fiK3f~$J!@X%h7j8#;!|Cz_0cjAt0tmV9(Tp;RHS|IK>%KL4&UxNF6xG%!J z7&2dsyD{YBBRq}p z83aGV#}J@G?4t;UZ1$BzxC>!5LI=VsCi>o2Wjg887(xjXI;i?4?-P+m(9LL zv&4NH;a>S%gYX?DY~5cXJS5|vMc5;sZ%5dG(28&?v#zfobTVNtS`aoPY(sbi;XZ_? zBwkp{^C@P(+L`D_WUK~dTo1yx5FSJL9Ktsd{sQ5eZefMu;$O z82A-M=tmf5*{?4k3?WQpN&A|--;eO;2oECc%7RDNH*&-~ubt;ef94$N`t`)Q5YF@3 zf{rtFxQ0J_V8NyrSo(h%``yf)1;Xx&^xw^FED&RbvJ5ayM6+U)a!b^z$S1o!ckbmG!mH!Hs0en(R|330Zs(q{^Pvoy- z)@3j;E*#hJE6KQ(`Pj59??<>#Y3ZK<|NAxAg+XIfgMUW+-sWR(&lCCo7U|{xR>M0Y z@mCJ`z?*!0Vw090 zMf#CeA0NLc{T1AOTKaXU--#+8?}Lty~u+)rukU5*DsqqiRE9u3|~^82+VzYXALpC-SbBK}1Uet}sRjf+R< zL8I3V{dqydKS}ie*`iPJhp{SKL&oLwEVw9*ssBd!Oy4$r$BGF#?QC$uv6 z3qiR2XEofBAo|m!4?nf^;S}%2Im2P8up|};(v=- z7wISYLqpeC@*_Dm;C|}b1 z1L#kOE&K8oU~i|GGZRd{dxNImzk|HIT6=v2^(|`lu}axLCJ{e9XqA5rGXCbUDt`%k z8F+&o*7%o?{3kX1HNZcC`jkt0tC)3>J{N=ky;^;Kfc&R#u-fY(z|&Xz_`Hzdzoz;e ztKpLt!v720k89Z#5H&$>AwcQP5Q0D z%-<1?hMC_l7BBqWfp8R1Fq8;&hLfpK!rzeybcg)mXh#h3Sz|wOuYaqIuZskd$xxDY zg?iUSd%F3(ZAU7^Vm+yNPl|QmUy>zLiD-LwoZTBrw8fGkf>W`3M09)y-6=Jh3WAr8 zcp@B4b+C?DJOrpS#Pfr{1Pk?sQyjS~!EecUlt7-)?nz~aC^uffvi{@RX{4zNKi_Gi9jkGLqnjhY*Q3X-VtaI zsZ5DbBos)JUN{nD1MEn4$ATfop-8OLN)3d&XJ<;HbTiT)h@>bc6^?e20>nt0IwC#E zF01gQDFy~TYwrpqSa+x!u`Jvji1vUCp5n5e;bbTg4n%YjLLi{7iO?TNwui%9VNwZJ z*A;5Nr@5!Sog^=WCS@xCuKadsHd0F`LjmZWJwpf^Tt>pNDUWA#loP#wWK~23GARUL#aEPYnkjg36N*8IEjSuuHO)f z#KqHHp+u6z1z7>XABlk=q1W-88z2|f-4^cVy*^+n@;#wMG!*d%IUjzg0PZXqm1f24 zJt8?8SJ{GG^!D6LM2afuT59hbGJ&gZq6eIsR>U67t7?zBFA;{ZOoUAC+j=7R*z@z4 zaLjrZd1_&0o4Nin9>LQrBwkh$+A0-*3r^ugLLsx>?P!~XzYD#MCBvOjbP(04g;cmE zQ0AIMXHPeDC@C68G=kK1u9>D7!YN%=!BAUIC&Q={++4e9v&`0M>+0>iBM}NlCv-hg z1u=y9LrOWF>EWm?S+FWKn2|Z79+kW=48zdS6SyxF=3I)1<}ex{phaw~Ytf)P!pWeX z`bp}AdU{VFn$i)PL!U=fQj5{Ltca)!C);CsmRNL$je^xyGJ{RYB-Kc&S=z*e*^zWJ z1;NKtUDRG;U&3GisVy~|H`VzYH*NXkrY#%MW4AWe`#0A#G5^NK+iPnY{kLz}(7e9I z-%?ZCxZW>YZq@3FN)RBZv%TG)Bx0+XwcX+GNQHV+xCH~L0P}aonLi{R{cXu43~L}k zsd#48LVU|)?TtOjgm^%AwgCPRYY#+jOu~YP*v7_9wRQeA6||Jj^0NLh+ml*>6aSf+ zqT;Mz&&jepIpdu;_L67kusF$c#9F~Mi{9+|IBVvN)&Hy%mxIo1PSiyZ?L&Da>F5_lT zt~@)F=k=cYF6Zr7-$R)b>A{SL?VT)QW8m?Jlit2>WEP$^_?FVlj9E=ibMywQUZPI> zC#inL>HbXXo3$hJna@+49?4vWB8fMmbNq1rKH~NGVb?HYpBnn(jB$mV)p;lS`ZM!@ zKY{W}(gPfK^zeX}aEV?X%lEdvSvN{_P>xwo;-gsOqZoV;53T+EnFXLnw2IRYW?K8d zks*3SH~8irNp~P~@NoYS@!0}-x|wkq@*{4*TfhuXzgUwg(=IoAXk;PkRa!}VYW+i{ zo;JkR4Y^p`NIqL%l*j(cOZnyj{{74wMN`G|8No9%E-zc*X88y+?19STGi}}(R$E!f zYRkb#(easn?{{R}e8lC0x2F+Tz*qPfd(ECk+gc`Fy8ktBNha>=g-qUtObp1RgdH&3 zXD{knI+$jnT?f*oY1+^9xk)CJM)7&f+h1Bt`==tFUFxB!pJ%zJj!{{l34J?Ywndq2 zi=i@6?s1e`hH|~^dy@ux-d29;t|><4m!?MjdpOQPi8J-5AFf>Ier>Ya{pw_u`=!ZB_luL|?iVJ@+y^JU zZq(I{y1G$Ux78Qw-Fqenj@AxMp}k7?rx}&ehavI;F;xi{k4MSIov&mX`V0O#Xb4hBl_O49R;J<2;-K z&FaVOc`pVZmHiI@t&Q`LLch$$gHQ56>6dZy*E~dd3T=EtmVK0`ekiEQsTAcl_{(44biXGjsIjN)!mSFukRDQlYUS- z>Bs``%*#W(Q;w8(z8vC6pNrF9hYTLiyz=JVko)s1=gB3Jfuje9*RcK@oL=6tnNaw*G2Z#}jgIwaFd*-K*MT852A*<{PE7gMIdRVbjEWC&~s$T(f&Ekc4DBrc-`|}wZ zo2FdU$6F~wsb7~GO)lco&3?b4808HdZ5z75Gv@j>cp&{E-GQCUz|Li0=Q6N! z8Q8fD>|6$RE(1H4ft}01&SgqHX+8$|+`{G#E)%x$2*!Xai~*If|K+g%Ww8GSY~rr| zhr#Qz{sQ7-B+atfm#$p{7-L?^Z0j3AySu#i_mfQ>~8@|NK4VWaR-_D_;L(vV#N zlXk9}&P>#}Dep1(9?E`{@G{-QyLKrX;zP498>TUoa4%=HWC_|PAM?0Ao|1e}+9KWu zjD3oCr;TH5em`&HZ|vcDFn0B${u$IigZgL67)k)!oP+hB_?f*gt*awOZ>pIP~t zko-_O)sy6pjcDt1kL9T6Opba^ss0Z=pn6i?i`b9cRK{gC{vYJ^rFs*; z)2K6z_r$NWp2}w+yy|Zbn0RFlUt{Fj_urn|znQ(BgR-9J>t3h+JL@`Eo2I{c;ca^O zn{&76=r!-ZzNd23_gwm$C-wLD@C^&u>qV-cU7e%NipUOsYXG=Yo@{d2_O0`ii=sCQ zTB6;v>%h>}?_VZ|zm;7k`8i}#S|-N#yz}VOr!KIbRIke2xpjTK#6G{kxH5S^_dB0l z$#t@f&MA&(XuRR`BRWXP8ir`TgbA|8%@#hqh`Ii z%%Dfu(tMV`lupw6+@+pY_MbEVTI9k!@RhVRC*}SLS!Fp-Q}Yn_|EK)bTrhvZs|0_e z;BOoFYhf=eJ){97?l6=tk-OQPg3NsL$Rh z&WG>^1~TQ+M{LcyP?f#D615v3pR)8pHReKNj`jXm};c6Sl%S>^^j-^yNJO3#Wn z(#h3q?2&Bz4M_ed-Gw>u%^1IDW<1_v;&YInll7E<&!MBiVe|>^E6uRwYf5BW&toGa zGyp>meJS86p45L#JhHhscMA4}*EOXT@IN4jo;#kK|3Xp%<9Lyp(}nWS|!r=tTy4 zk%3-hpck2H*(Y12PI;~rI@JZ8+6kQ+VXsaOL9Yg(R|C+iH1z6z=v6QDDh0iYtMvfz zMDxcL?4_>rGO>Nw+D@fNn3A>krhvmHm&w zPCZQevvMtaVx<>zJ}RdPp%wXq?1{;0l-R_7Y#O*`ZL**;pPtYC8s*b0=RW13 z-J;Hn{gV5U6&>dDko@nhKv}?WF1cwxCIAG!MTAc;v@# zLq1{eTA7+F(A-ePd%wM2^moXU<`kboTWyCNc^-qg+^}aCu{u7NQFZ!+~tT@5O9uk)R{i7pim6=>iSa*DB;Z3jOAhE7=o|(80;l z(K>cL`})!nF7sdZ&zE#`c}#peC}sTm@ImMT&5!X6-@Sjx4L+#-hK{xkFJ+_?W0Ur_ zqDJ>x?_BhJ8a+4gh!XYf)#%fr|GYk|WJC3$ zaz4cCK0c}HT>_g!Gz{6lY227F>s0TdI>F9)$lua=;4+it+skR^<&b%JN(Hpz)xTdsbl2d9epp2aU-~o`t$;p_Eb8?s(hj28M1wJ z$Jmg;*pR{40KI!LgRvolu_1%8A%n3YgRvnKmt)H=Ikp^Jl6|dTAG2w#Kiimn{Bh+| zjKHUL4=)6d<&eWfzv6TK5T&1$GAA5@-%1PG)W6AZZR)evOZl?Yzxenkc{~Ij>%pTB zD)z$xm*^bA6V%Bg(*7`~t?} zgBg@zo9mx>T+AsO_}+m_tat2Uu52=@`p$q{GpK!ZTi>IcADRQ?u_EuO{L3M~vB@IT zrx=@X4?^tWmB742*^-?Pzl%}MiqNppnzck73#J-LkLkcpDV z+&CvW4z=kz&$;JQxz@>dOaAy;Gjyr)$4__{u};|kj-kpQ?-?qo-a+4Wnuuj&UF7BH>Rn;+N{0O(uh*19BbM%q1;9NV()QW+NHg+yixFS+O$3O+*U2>x7w26jtxqgh%#ZxTo*8|1*?7HG0n*v!Mo=fq35oVO7{G4Ge?|b`YITf-T zi;n@h?(bO<;`0_4UxR;V#+dKs^9^GO*=A**Y2TqT%RHZa^C{Gma4yb)a~A24A@uNI zmc2HEuV>~?x7Iu3i{u^}+NB!pQiXP?leUIkHE7+b#C~YO%|Hvh5)KkU|JVJO~X)5zee4RzmSO6bY%%Md;wq!7m{Lt?X zcu9xJuO?m_zPraZMpIfu)`Q#J@6z6(&)f%qdM{D7h{~?-7iGhza;fqc=P2KeB3e;C zl}$3qRW_w%FWaz{z0)k)IIpsI%CdLPQTEOpWuvWO;}6jMitB?Z}Dr%a!0Lz;Lw{#{{v_5Mom-abx) z_Wp~=_Ekxk_AmGLJ!twW^Wdu}zl7>5#yhr;a2OjKTFmo5V9P&pv~5Vtm+Rzw`H2bQ z?Fh%IpCEj~QTzAxOzg>~(vN1yZu|Ph{#to@C+?4BN(Wo%9aD}&c7gmMdJ?vA54XFX zvBN8gH|5I+ejglGY`#o9m4kArsX;-(q6D^oWGN z3>}@4e&Lt6?E5p1a2{$g7wP9Vlk!@Aw%ErxHdb%8`^i30FQPe>eu#7hd0)pojpo~9 zlX-H?r+h^i>y@k)@%c2(r)p8(e;zKQ^(#*sYnJUnGV=n5bcgazOPRqI4cDiY+~%#I zxKj?D(tTfo=^Oy*8P&5A^l83Yg7KMfh@PUW=B27l)%Zm|iLxt2Pbfb~@W*cgpzD9CWE|T2T-3>9gtALFBtz>IUtv*>$O3(K@tlI{zcklA_jY9{K=dJNQUa}R{X*vT% zzF{qVj3=c2jZMyzdC$so9`mFPbMd-*ycE=2iT1c??NFa9EtdHe&VSB`K5}A)aoF^9%#*+>mMmN(=50 zew?r=xoM>5ps`ERIAYOoXQOd&4jRWKjjD6-acT}4>`6g`^ji7TQ|T{dXm6X+OP&<| z{Fy%8-`G#X@!|CklLoz*>qx{`6eJTh2 zy)yl54*G{=`mP-LkH~a)j`B~+^mSSrQu~NDbPuaGoCe-}vkm7BtM)5+YIgg@wf6Hq zCFsA+<6h@$ALHEiKWO5tlQ`S^1P$=&LNA4Oh2L3h4^&VCH<6D7tu$B z4c;y5Ql1w17w|qYd;WV!mQ&Ji;uUAZJ0|hU`{t(ij>K!~Cwoi$)TZZ|>mI#(a8-kZA*M)rz&3wxit{DHkz`FkDZQ@sw!F+!bbEreZH=T6Cv z`et&U;Us>y!*(9qE9cRJX-@YY@;$ge;@Wq(kDnJ}YMVqH#jeFbwm2 z&|LYCIP#x%1lTIAWo)vUi-jOTUbbk>>dH^~dQ zqi6c;^71*#qI0_MK&DIhns?oVC@+}4&@!jBGN&D7Hhw^5(m12a9O}21x$Ofgb407> zh@+mt52#F^R;JHUX8Z#xlg3z8=3z&lyZ-|!GpMy^(9xa)A5fW7nrx>WvK{$=$~>#d z_N+s;yFZ{ZcWL$9<*4WW52#GH)}C%hd%p4kmAPN5=YB^$kG+qvVx4ombI{Pot9X|2 zD(-0C)6ROAiFz-eVUoc_tH6tOIV#pt)Kv*E7eS`K*`TA0l7fU@xs))Mu@GfKKL$Jmy)KrZu^TGJ}v? z3qRNJ(j>iaQd&8c&vAe9YcJU@df({MyfzQz`V&$?niRl;pE}M*1WNDk9&6|D=-6B72QmupB2K4mR=t&+2#4%Uv&7rVX|#^z!Z(0-FpEB^ zblPUDo=e}5UOq=U=_}ctT-7kbuhKpuFa{=Y75w+*|}*4ZqI zbEcE-kv-Ap>O#J5&ZpDX5WUk}hup9cJ{Rm=2KFukdv}gA5hwGTq`hqXZSFb$A+qi1 zDb8={;7LX{i)6l#waNE}zLZJ#i~6g5y+zOdl~?#?jOjL-&MWJ7`mKM@b-w4^=hlm! zwcgV$>l5eJX-#MwU!M{<%(K8tV~mH1_3}nMmt@+e37{O0}b zp&F1!Dm`NAFSQBzKF)ojvI(`v!N%;m@NiaLXq`~+Fg%R$!{w!YLfT6UGV$($I%{(& zZ^Kdkp1FFD#d-_`D;yhWg)?@z1!F2YggKdzH|KGyk<@nM4L{0GsXz2s|9)<4X+ zmd^gk{lKwg0qf+{}(4l{-PjZ_8x&8f&+RGDr+|xG|VB7SsR zLTm1#&BeK`f_=)SeI0WO+8=!cxL>1pCtS2oIFEU}v^UCa0ohDmpAc`~@_qIE-3FuI z8uz#z>!*G-KCI3c%*=RJkR0d9v-=C=++qp$6>8J)L6;JIv4kn)^K;e%{JmTyPn%uY zCv5peGM)6glFdF>M!3DQj-)q)b3SL2-ukuuy9G1{G1%Bj>cc)ZmKjXbyJX@_zzo|{ zNndEB_bkjIR&8&2_$4Kbu^VW4tmZ)j5WT z`5A%M!&NKjo#WQS%ZA{8eadjEJK>{aaotRLq@si4xo>!dSHx_b+*?- zjLR#AFJ?b-tNt&>mIwK}z-nyK$BX+X$C7jEPZzRBmWg$9{stKujj$n0ZpezGDFm^#Lpn3UG=J~6QmoOCeit+GE=7>m zH`6qCd}5NXk!g5~Vc+2Y&e!$6G>2YZL31s%4e9+NP46$yq4z{Teqg|RK03emz-*lt z@_HWfI$-L)l2h*qA*WrCQ!C`O9sbTXv|$U{un972gv=VU%wtM&tT!Dwpw@Lr2KoJG z57hEGHh;JE`C2}&qvu{c8}j)Op1tz5J2z77GF}Ha~()x^?7cby_ z^Xpmroj6CnGjpQv5&lj}F*~r5e1ZLGv7Sp~K5s|py_>CGQ6;}Yrsmhx;8m18K3w*~ zgDX^<6Hc|+pT&EVi{Z<9<$UiV*p6SrkDzi2`lvnUV-7yW*AI#I0?nT2b=&@es2gN3 zybbiz#V$5g>SEU!55S)FbK5v&xS5jWL>5`*%Q{@-|LMJBTj6hQ;<# zdq8h9(Ay037IXRMF_%ApxxCQzE~)E7|Lwx}yGZY8kC}9z^!RP5*QCd^*GoEPoTrX8 z@%q-KiSNr`;+v|u{?Gu{IsTFqHz;wkgl0yUYX%~RO*4; zuTI|p9C}AZ;k^68PgY)H*1xS^?E6B~Ij;l51uy<&g+8v4zEeLSohRKQeOK>X(%7x? zRloQvTfSb6FZ4XFQ$(X$uG806y3GBDu7V)`1q(1R0w zc0Fi)(Xut;Vot+xWV?^3cNLZVsb3KNZ0$ZEY0XvsI!F13vXsB>9OYAApR0UICp$uM zO+R*5DSIM#6aD-DZ#Lz`i|4i}?`MCg{O`_hEam=(^xv-3(=6y~0F7t+4L^}?Pekj&fuBG+eU1EPh zFQ+RgM~YB2q+d3>FS z`-|Xtu{rmpbtU_r%=k+KUT56)C#*O%Z#yROO82H+(4&>msg?V=9dq;V)Y14~Iy&n5 z!hRwUwDO+#-Gk3{~4Lz%f7b42VQc~(CbuiNaPXcuWHi;@_nyt zcBOcZ{!3}=r%|_cJ7079!2}WR=}={JoFx9Q5?2U(I__Uvo+_S^9L=5 z2KfFRd`A!MyY-sw#Mg%T9C$I)->tXu4;-@Z715qxo|GTOne*Yw`ApHIxiIXSwm)U{ z%fpBEcx&_cccEO>yiOa3=uD8C{gUtR4npQ0zGvZ^Q1jx!K70Q@=df!sGyeWv{T*4|uD%5w0idxhTq zG*%t~t(|@JttIg;NIsj#^Bs|8TrAuCEjgBoc3nR-X!f}cLx$GJPaYibc9VZt=3{%g z&+i(Z2i^!@@p_Y#7vtZ_c6o1vf2zI(D*V9Nd-y?FA0dZ%!&ENO6Yp@=4h{4T@OLJo%vHN&j{wrRlibW=gyUu4fPN zc#`8usUK92?T3HuB_71PF?BJNLw`@ez)P^m-a$S_xjB5<&i$n_lmRmdh7>sJ$NsD zA9+N6e?auBO16i$8+}`YzN19(bdIWAeuuD%J^u)OhjwPB$UC34aNf(=SY{Ec<=<9v zvDy(@>)zG>NQUMcG`EUm@t4)ys_F+qMubx?zwt%qsRZ5ejQUoSSVJvZ>a>GIhiEqa zV2`O6ProPh0{-LD0={<+*$gt*CCo+Ns=%1kv?R{+xA8eb0p?~-{z~Py_;ySF2uI`@ z<7=@^Qm%B?=iL_uzj|$uUKb%nxsv;7P$0|y2^W%r02tUx&$;Szo^F^PNU?9f`)!JfZ!Ib zUoE75#>D(bd{5jprqgJVG(@=%oB7CYmood=q!M{{Y2b&>_s!iO(0lBH?wP&^GUDB+ zV0wtJsnYp{PBai7JoKv#qZMM>Ab0mKb%(a?P-cD9jRlm0XHl( z+jd$1PyW|zoA$&1#XS2Vi!0Gb>AghdGhdE;1eeQkcZTodwDCA){}xFcqD^)#*L&>K zQr7Byag^l|?->tdTB-lMI^b1jM9a{33Ae%Qs~h;(r@!}J_Uhd4y$gAZvdU2w$z5L` z7Vm$~=CkQl!6&7=_&O2wtviYgKId@tUnyxhWq1j6a^O|FPdR}0ScJTE##OCtWy^ce zk#Ab^P4Dm0dF3jHuRYXnrPKN3xMTbY_M5(*ht4%S`&H0^@6*boGtQ%q@}~MMdUQ70 zNpHIY|8Ux(N9Uv)9Oa$W(&_B9&yjzZ#t+%uR~-4>TKz_{)UVe8k4Sp7hC%-F;`FgPl|zS5OWmOK5|bz2D8f z>Z|-O#al@Zzq0H#X^Vu84`ZAvru@s;*pe!Z{teO=(s-l%mJxHj$%hYASERlpX#lsp zFH3pgDe@YPJNF+tekwR-|3U85Y^MQWr`zxldX33}KTpySEr_W24z&=L6 zr?PW27dqD%dge829HB9T_TQ?f>^9+QZd=8A^mb`y_e;H|vHXa%rIerEXQ`i}_Tlg4 zm+aNj8Z^3_WV@(#Q|m1)z^OI$t_|LvC{NL#_e6qI!tYom>vttHmMH!f%Q$LVve}U- z(XQ&-BZ>~i5udtlENAvHMCsF2;Ae!F*_K{l(zoaY9ds_sL5Fnr(3IsfWy3k;kiBY4 z@tairwr@$QzC?Y9zV}3N&Tl@^+QPs72{y-I{Nnq+uVp_oPqf_Nar|(T$%9yfzH*rQ z!XIarUGeUWXNB{(teAFrP+n>8N&bG)1S|IH@Uss183+8d10Hw4V-9%K0e{BVO|{z+ZL1Uv|J>a=;Hb;Isq&3kUq?4mjn2KktB}4!GL^haGUI0}eUhb_eWtz@Krz zpLW389B_*RZg#+TIN&V~c#{L(;DFaV;2H<)bHE>Wz_&W!wGO!20arQT8y#?k1HRq? zuW-O#2fWMy8xHtd2YihK{*VK{(g7Dc;H3`uG6#IA16~A}IWGSF!yj1og6u@L-vMmo zIO2Bzr%P^Gx%{2%zwcLgC;h_ZT7!Nalztmn{H&$%nRRjcb=l;{{>=Eys&=Cz90^4O z-Jw4#EHqYil^d&8b%i2vBg>WQ3MUOp1Y(E~?-f59I}4HjXsMx6>4&Tn-Tap|jX*SL z&`(Q)!smzr{m$qr`e9iIg+wUTlZYDPw_lBDtik~AT?YNCsbT(>X}KbGFOdq0-(Ut) z^s~XnmOD2KQWD-&Z^Sx`SR&jRjs_wI$-^k?20wvREV1KyP>JhQB4n@DW}Q~M_?c#7U3aqcW1wXch#B--zD8MBs8=x+ zNE+hztBJ}Qkp;AFGBcZ3AQ%jb0u{leMR8RWirpUT?gkVJ60In46b*%fs}l0}p*33c z%d&= zhnvgMJ&lf@1kvLuu_#D2a6`g>3V0P)Hk***aS#s}$#|$e3`s$g+e5*gLM^Vr1}=Mn29M`DD6K}I zBSmT#i5M|W0h6F}hhQKK{MlD97G0S#l3lU;sO(OVO~R-f&^r8A9T!CImcv|IEICUx zN`b3U`NMoQX}`&cs%Q#ZPwInFz+vg;NHN3(a-Sn~XpT zwyv!w1=Cd~$3r6)y`KDocCJT!R9;1ngRxGz58x*fgOQZF`^ zHrHA{hb56pvY0)VJXA>*ElG#++fa}%K=}fZlvEr$12P|yLy2gBn_4>$GFM?CV}-^I z4*zZ~M$c$4BJzk{k`(ID4YtE^q;O=)d6#t2mZPQXt~WjrOZEhedUTmDgp4(ntJe@t zZ6Fy6qWul=Yvq+jS!KoQiq&hlt_Ip-_l8I_N{yz*^)=1wjjijOZr|EsY*~M|QFr^6 z4VyOJxwWQc)9qWDjZ)%wb;X*Bo8hZocO7`&isq);YwaZ(ipEGH9|=c8g^V{mn+sp4 z2Gem=GSqG@>PLy|sduk6>S8^SAlG>EJ~;*<6tY{dD-}3g4~YnOltU1E(o$j))+%Fj zIGH328rMJH6HA5G7V<)jh3tZ9+#5)Q18s<Lc_nQZS#B*@EA zzMf50D=I2XxlqTMmCKG^Cd}jV;Pn+oO)}ZjZOK`otR{lSmY5@2sd+pGcB!W1RQ!s4bQt zCM!ry1uxEA_mLk6#(<#8zk`o(#9}HDGkrcGS>qf?Vv++k6c^R+xE9@UUtou% zL%igmA=xqc`6M|9+1YW*Bs=OkB*S^ho>Q^HS`s_BB1yNmnLBe%bkY;*<~`AHyJ%Q$ zqhymOQ?YnF6s#~dLkL{~SU_RiLb{Bk`p#30lhh&k?Aq4ZL>`*z-Dsbs zVLDHZ0e#SD$b!m2j0&N$F*jXaS=GCi{)svnGL=){rb|tOd3w3ko~U7B2Sz=8q8kaL z8+M@^-j|>;0Io-8s*CJkjl*P@kh2 z7z|>Jv^fsDDJE~%%XYjw5KgJ==8~e66WZn8Ks21}Qa*qq9i0v{*tU?g`w%u@Y5 znr?{xQYZ!+TN^LxNH}M$$B;CP%cs%t_J2ZLT)9PT|r!C zSBJiuBR#bpwcCyKk1?fvp-?wp47K@lpiwVo)OT@5;5Ngk>=o{UGoHF9HylpwrqJ4W~Q*7FS|CU zVhM~9w?`v8Zd)Ek4&fG3QwdnOtr{GQ?yw-n_>iMTi0OnCfuYd^xO=bR%4v`pvzf14 z*6@`!y^K0qU(_Nti-B#uT#CL;$-x<~OG2jAiN$#-1m=uiVPiB-3H(GNmVn(PP7l_ct(PMlyk$&om3 z&9;s%vzHrujJXd(2)Exf+Q_BB!dZDNa?N7Tg}ks%>Ca^UDp>3wFF{$|r> zd%v*vZRYIH1UuB8={m3LyQS+kJ-6w&so$1v+x2>uPTTaE^eI-)cRLzmd_~5o!wx;1 zrLRssg}y~Xfh3wc5}>Nt^fjA4%D7NU&hKpMLcCHt%SIRJQfHY%SDCJ(jNKhb&`7-2 znAKoL4Ao6wNJ-k$O1X}7g+6!p9re**L-Xym>o>M+scGE0DRuAK_}aVHHm$8&+r4)4 z+BIu$T6^Ex#M<_?;k7+$18b9OJJz2NhNVGUPxIPLdbA1JiM{aivDx6T(z4B6X0ppXhpS(lSNnVW{0*K7;3-9<0%=!uXU&6Pm4uiYs&opLzqU229V z^-r7EDM9Jy0nW1}rB2(2BkG0LS{QHN9x|p;xxl8a&p6gxXVby7Dcb^@lZ3-osah=O z6A4psgbQW%36iOKh`}aQzHwN#oK~Pxq2%>)KVfbe3+c)&7E8Lwrv_hh|AjUslw4`u zD-;hTl!;U3fqM&DR~K4A1aH3i=9{Y4RNd53eN$U|uzGc6W#t-#%4+%-=m=Jd>n3?$ zU1__l{I^tB0jGLRj{G0Jfc(}a@>8CVIx^0c-&sE3H5bCab&32pQGQ3tT=}z={}Gwt z{g(fc_Ug(kmz6(QUDaM$m0kX;D?fV9`p=P{_^-Nv{MKdhU$y$C$}HD;jl971UTtWUqdfV!0n@yh-JKtEqE6h_6S+sX7s5EyMq*+qjxt$g zcJKlXbbsk4Cb-{iSu$xN?AEi~NGkpU81vQPCb=uk=T~>C-Cvub;_HC+8p(208|18C zDyCOT)K=QP^T6kr*1D~U0a| z4%ay(c%gbCT{>}oWrr<4A3!#3x#P}FTR&y_Wtual;qs+1iE62qhFi!_z{q0w(xNz# zhxDv>#T-a3 zW?IJ|qz`$kWAT$u1|%uF1UN#5s^02Tm+IYZ(W*|IF=@)WuGHb|d81VH=gqmGt@; zNiIdl#*?X!+qkRgZbP{-RE)1r-Ds|#aB!_ObT?M^R#xl&uh@m@N%9j5v{YsGP&AOE zlgt_|Z!I*$Wkm}9!!+M0r{jrax7tER^{lf2viw;FnHxFY$71G+NtXUQdw@7s##v)t zQVn`#v7XfR{4|(~2uFG6S#M(U!&XF78y9p#TPWAFG+m=vZa9*>RV}jIM|1hMkhSh3 zbZ-kcIZF6yk10nXWP8Pl2>b3)b`uYt?hQmRG2|T#?b;TOic=9{ZN}IU>v42cyU5J7 zwP;K`rU^sV6$?t+%z3h(-&1=kwtkm&V}+JQr924P6oOi@(sp$$`%{DAScw#-UTx@` zV&#&s1ZI3H)&py?1ISPkzK+u!!z_wAo1;M5rNy`<v2O+j|n!FgDl2wdXD)q|?p+183F8%#||J(j-&jBx3A7ivi$S zibP^GJw<)V#M%}F{?;D24@o*ckm84n`GGch<{+t$OY}Ms-Q~+d%>EV+Ghyk~F!^4K zN>;E+Rxr1tL{}O}^L+?Cu3awHJK!~0aJb(t<_@7%LAhu@>tzeXp`lh`jABBt{E=yI zR50z82&TQ|z|0pHK*Kt2k7w%yJ+lt}Gp62GAinfh5IFD7HwzGFo&9Ib_P&Xzhd69c zc-Gl&CeOVyeu$pR5r^2Bb&i}db&{LPHDBh!vvxe2an)7_*pX%qcV>Ck-h_1~nz4C9 z?t;;OH`w+(cfqdz`@g?NWi&2+Z>9^O(R1OU^5XYq&LA&C7{oKXSi~>F{W#(dUu@y7 z!?O=@UffUNesa-!Grf3@<9-5gvBaGM9K3jTSy{s+@68+oZUe$$&?rJ&8gZuqR}mk0 zj$dNo8^GBq>$-c9xQ_$g4g3~_a^T*N_!QL%*M2u+u8|dAqUW33@3AwHYx&wIhP=8Z zRd@B$3x{boe+h20-aS;;3@-gX*R}s}jSZgBuDm=8R_R4Q@|n08=2(dalP-9FMBj1~ z7umWh4JIQ|ufcaAv}kY>LQsS25KLq6ud!N=ag^Py{f zta62qO@GzL2Hvb;CvlHd3;g4FW@SD$h5LSlQH09de60N6YS;nbjsQ+0#1WbRx8lAn zjAz`BwECD2_Y+k<*78y0!*lsHK30bKk}e-Rg|ZFAc_{wZHEbX9zxbx?hlqOt&*lFN z9Pm2*6_i7~;eH(9dwAZD@C?Ef;Qa`r2)hCAM19`w@v#xggOK{Pj}aCb++Bkn`8eg|21%YAI`-_)>O zlvW|iGj8>UAy}&sQoteHGd5)oeNGrYvx`F$Cguz=N z1Kgh^+2THW6!@Ui)FASw5f?{jMQ9onaa31aBkyXq)j+r!;aYkgnme6%$#$u9(zm(N zhj@B^`m9F3OiSnTVwc#`E4B2UQZ~*@r5`#+x{&S6lwLod+e#>_ z&z7$9NBTThIxCwyefk{fWJBgkKh4uwPWh7V&XrDm0P?Y=w`%F4U)bfhOG_u)+MJV*{Od%EuCzwvwqvObh0~h)lc-j z-1<%Z-ATVmODB6dSN>OcdhYVc_RhsW*`T@P^NOF+?fRk1pT-hAnvYy zf3l%$j`9b2dT#n;Tb=UJ3*}>0I=u!?W}2>13as_3P5oXxGD?g31 zbEQlBp7WCSJ?FKtu5PVSwsFgy*Bdug+*GkfkEp6xQ&GiOMRG?sX8O3N65`&ap7`u6 z&MFe2PWo~Os|fCh2D-!TtfDKB?BXCYi+d`;DmtS*6>9db1O5cj3a3~Fy`9S{Hg9Tb zMsYoDNmhZGVi+aF_!>q^zCL%>`c&4@cZ0PT^6wmGSSyaXe!I z{FDXoH46p3Jc;9BV(ybK@un|@?{O((&lNE9QuX*G<8!na;XVE#9|B{a&a=vO$w0Q} zJ&AYrDj$NdS9-a0bxN4xNr#I|80%%$rSQ5nyb<7y0B`CZ3(q6(Ne!=w`#-2}{%m?* z)bOZp7(=2zI`JN4R{O?*7YANtwS}k3{YwpR^e}v~!xjy?)bnE!k60Rq&o+Kow5JpA zaSd+*coRAB_Gowqfp;)RxzA{LQ^1=7-f@?Or|Pj^!y9=6{^c9l>3!eA^Na#-6nNtf zdW8F`hF1@Nwf@cQ^nSub{RoeI|iX_Wxk;yI9zBQZLmn z7cx7&z3*V2T4=#~9i121Bf9b>R)YzPl3Pt!lt6Ywz(P7?F9a-lH`xUN3qex<7w`gp zr+zMAp{Z(2%ICEZ{Z_!D0%~rO&n*-)$EbYS@^KSh%J!h+Yoso=}`y}^W+@_VZZU%~V%-)7kfzLMXUnec~f z?dE2`o3ZQq?`DXis$U-Jx13q^%R~KWtx3rv5AxW+tor7mzO*(pE#>WkylJh2_!atm z1-G%sP5dR?9`;^s;eQqPAn=PNzLP()V|z9J#+kLYNBkXNcK%*tcK)azPd_BmsXV7V z{>cIV2krxy=en8ThcdhZ{*Fj^3E&Ye{R+T?8hjPtAq~Ek3j7J|n@iNcJWu$*5y{VO zfP2CJwH$vj+mdJXmpoTKyF1U)k384qESP8MGig_Z@HO~Bz;O*80-V<1M>GMDwth2D z%qb2RQhFhKI?vKy4`TK^@V^h3)&TN2yo9}h2U-{COU!9UCH#a-tcxgqpVf>f^{ZnpF~={TC-DCP@W{qmp6)??egXKTR{p=Z z#GGQcr2jtv|3;HvK1|}YhQBah%;72}{VM>|I*yXBkuU6no5M@l%6u^=IVNv(d{$RvN+D&L3tjUpoTw@FV?pd{`1-(#PtpEgvRfW0sA!k6ZvAzVN}w8m+GsfFGNA( zTK=m5@6q5DfLk^ERe)&@?~(fSam|S!er^X$YZTk(i}cR|-lf$ihKIdcd+l(GHIEia zZxC=?D{ss##;+0%FJyZG)0&~6w}ib&W^xwvPM8iJ=cvOSm zb&Iu1m4A_PsF~338vze#^;rjazm~s#o|t2{%JS|4Jf)@o8Q^C%`q6n}Jl@U({=I;Y zYxoZVeoDh10en(Rp8(vb>HAB7M>YQbdY)J_X^{Qp?{J^i^8d#?u_o9n%m0&BUuw_1 z`If)nL3+V_u|`%T_4`V4LB3+qBj3)W!7BmNn$uen|7O63mVO&xT5l$XiQmlx|Ez{P zT?Dtx7jyDdzR2GOxKhi14`81LC+CYbz;~Fy9{}8>r4IpqPLt=OfOl&0dUC$yk6a21 zydQ9@mj5M6*YJM~_<4=qPXV9N^z-lMi!~)B-_wBO8ofWx7i*r%9u+MRYe*+#eXj$Y z*6^=iVEI2}Pj3c1qNT4}Al4{~mWcc{3&fhOSC;=tz`Hg6w*em2@<#yg*X0R#N`t=u z_!+IfUm|=BrVm?xPlI>UJXg#AJ-{b5_+_e}#?OxySmSFcFpdG9*6@D=nEX>=4;Hci z1WbD-N`La<^1h&ze-XLBU$ydIrcK^Rzdr={MGgO2!22}#2Eepeqv)>#?9u3b8nB_k zt$q#*1SukJpTarq?Z2@_=N+S{$5>ZZ@=pR zPiy!$7K%NL(@fB-DHMAqs()?){EntScLJt8hf^}WgQjdl7QK54#r~$kXS8fbOLP_O ztUZxRrg}O$@aXr~wcNhd-?*u{#qS4HZ-dr1_&02=*}UFgyME)QEj;QIby|8b=I@Ne z+5!Wep^4D*vw~AMQC|W^O zuzI%2Z8yucAGkx+qCMS>;bm6hht2hjPZ9TJ1eSgsa(zc^`F{Ovw2e;@sf=B#rujZ-h+|@2_{}T^~kL?_m5JlO1%-Cf1W;bkQKP)$G8SDX=|caM`%$&w^1UxV|j@6pPW zXV$yZ3Xe|tYxS$$kqU`3@l=kL;1@@?=q!=HBb-R4{Pe)d`nEmJ3v zS1cj>Y028>+t|3Nw$8t%qLRNm(G*A|LrrvsoJ55ti-baP-g2DueTi@?P6$OX$wb53zG79n0RN{ z@ve4vAQFkSTdYFA952d?x}cc?iB2|4=x8&){e+cMuA(lMV9nMdhG)Nouf2lb(GlrM zcFBWo_11}6v`KqvM?Brcta>6H|BuAX4sHGH_*ve+h-n3Tc0#F znstbtCcgP=`yz$;wFld$A!hdp`VJjeE{L%u)E#eXVl*nu)+)=y1|liHI%VnSdvW$w zTVo#eB)Z9Xl=K2x!1&=#t(9hf #include @@ -185,13 +187,17 @@ char *name, unsigned int namelen) } #define LINE_DIM 1024 +#define MAXLINES 50 #define BUF_SIZE 4096 #define DESCR_MAX 129 #define SEARCH_DEFAULT 0 #define SEARCH_TARGET 1 +#define SEARCH_BUSDEV 2 + +#define SWITCH_CONFIG_MAXTRIES 5 -#define SHOW_PROGRESS if (show_progress) printf +#define SHOW_PROGRESS if (show_progress) fprintf char *TempPP=NULL; @@ -201,14 +207,14 @@ static struct libusb_device_handle *devh; int DefaultVendor=0, DefaultProduct=0, TargetVendor=0, TargetProduct=-1, TargetClass=0; int MessageEndpoint=0, ResponseEndpoint=0, ReleaseDelay=0; -int targetDeviceCount=0; +int targetDeviceCount=0, searchMode; int devnum=-1, busnum=-1; int ret; char DetachStorageOnly=0, HuaweiMode=0, SierraMode=0, SonyMode=0, GCTMode=0, KobilMode=0; -char SequansMode=0, MobileActionMode=0, CiscoMode=0; +char SequansMode=0, MobileActionMode=0, CiscoMode=0, QisdaMode=0, QuantaMode=0; char verbose=0, show_progress=1, ResetUSB=0, CheckSuccess=0, config_read=0; -char NeedResponse=0, NoDriverLoading=0, InquireDevice=1, sysmode=0; +char NeedResponse=0, NoDriverLoading=0, InquireDevice=1, sysmode=0, mbim=0; char imanufact[DESCR_MAX], iproduct[DESCR_MAX], iserial[DESCR_MAX]; @@ -219,8 +225,11 @@ char TargetProductList[LINE_DIM]; char ByteString[LINE_DIM/2]; char buffer[BUF_SIZE]; +FILE *output; + + /* Settable Interface and Configuration (for debugging mostly) (jmw) */ -int Interface = 0, Configuration = 0, AltSetting = -1; +int Interface = -1, Configuration = 0, AltSetting = -1; static struct option long_options[] = { @@ -237,10 +246,14 @@ static struct option long_options[] = { {"message-content3", required_argument, 0, '3'}, {"release-delay", required_argument, 0, 'w'}, {"response-endpoint", required_argument, 0, 'r'}, + {"bus-num", required_argument, 0, 'b'}, + {"device-num", required_argument, 0, 'g'}, {"detach-only", no_argument, 0, 'd'}, {"huawei-mode", no_argument, 0, 'H'}, {"sierra-mode", no_argument, 0, 'S'}, {"sony-mode", no_argument, 0, 'O'}, + {"qisda-mode", no_argument, 0, 'B'}, + {"quanta-mode", no_argument, 0, 'E'}, {"kobil-mode", no_argument, 0, 'T'}, {"gct-mode", no_argument, 0, 'G'}, {"sequans-mode", no_argument, 0, 'N'}, @@ -253,6 +266,9 @@ static struct option long_options[] = { {"quiet", no_argument, 0, 'Q'}, {"sysmode", no_argument, 0, 'D'}, {"no-inquire", no_argument, 0, 'I'}, + {"stdinput", no_argument, 0, 't'}, + {"find-mbim", no_argument, 0, 'j'}, + {"long-config", required_argument, 0, 'f'}, {"check-success", required_argument, 0, 's'}, {"interface", required_argument, 0, 'i'}, {"configuration", required_argument, 0, 'u'}, @@ -263,7 +279,6 @@ static struct option long_options[] = { void readConfigFile(const char *configFilename) { - if (verbose) printf("\nReading config file: %s\n", configFilename); ParseParamHex(configFilename, TargetVendor); ParseParamHex(configFilename, TargetProduct); ParseParamString(configFilename, TargetProductList); @@ -274,6 +289,8 @@ void readConfigFile(const char *configFilename) ParseParamBool(configFilename, HuaweiMode); ParseParamBool(configFilename, SierraMode); ParseParamBool(configFilename, SonyMode); + ParseParamBool(configFilename, QisdaMode); + ParseParamBool(configFilename, QuantaMode); ParseParamBool(configFilename, GCTMode); ParseParamBool(configFilename, KobilMode); ParseParamBool(configFilename, SequansMode); @@ -297,7 +314,7 @@ void readConfigFile(const char *configFilename) /* TargetProductList has priority over TargetProduct */ if (TargetProduct != -1 && TargetProductList[0] != '\0') { TargetProduct = -1; - SHOW_PROGRESS("Warning: TargetProductList overrides TargetProduct!\n"); + SHOW_PROGRESS(output,"Warning: TargetProductList overrides TargetProduct!\n"); } config_read = 1; @@ -309,71 +326,75 @@ void printConfig() if ( DefaultVendor ) printf ("DefaultVendor= 0x%04x\n", DefaultVendor); else - printf ("DefaultVendor= not set\n"); + fprintf (output,"DefaultVendor= not set\n"); if ( DefaultProduct ) - printf ("DefaultProduct= 0x%04x\n", DefaultProduct); + fprintf (output,"DefaultProduct= 0x%04x\n", DefaultProduct); else - printf ("DefaultProduct= not set\n"); + fprintf (output,"DefaultProduct= not set\n"); if ( TargetVendor ) - printf ("TargetVendor= 0x%04x\n", TargetVendor); + fprintf (output,"TargetVendor= 0x%04x\n", TargetVendor); else - printf ("TargetVendor= not set\n"); + fprintf (output,"TargetVendor= not set\n"); if ( TargetProduct > -1 ) - printf ("TargetProduct= 0x%04x\n", TargetProduct); + fprintf (output,"TargetProduct= 0x%04x\n", TargetProduct); else - printf ("TargetProduct= not set\n"); + fprintf (output,"TargetProduct= not set\n"); if ( TargetClass ) - printf ("TargetClass= 0x%02x\n", TargetClass); + fprintf (output,"TargetClass= 0x%02x\n", TargetClass); else - printf ("TargetClass= not set\n"); - printf ("TargetProductList=\"%s\"\n", TargetProductList); - printf ("\nDetachStorageOnly=%i\n", (int)DetachStorageOnly); - printf ("HuaweiMode=%i\n", (int)HuaweiMode); - printf ("SierraMode=%i\n", (int)SierraMode); - printf ("SonyMode=%i\n", (int)SonyMode); - printf ("GCTMode=%i\n", (int)GCTMode); - printf ("KobilMode=%i\n", (int)KobilMode); - printf ("SequansMode=%i\n", (int)SequansMode); - printf ("MobileActionMode=%i\n", (int)MobileActionMode); - printf ("CiscoMode=%i\n", (int)CiscoMode); + fprintf (output,"TargetClass= not set\n"); + fprintf (output,"TargetProductList=\"%s\"\n", TargetProductList); + fprintf (output,"\nDetachStorageOnly=%i\n", (int)DetachStorageOnly); + fprintf (output,"HuaweiMode=%i\n", (int)HuaweiMode); + fprintf (output,"SierraMode=%i\n", (int)SierraMode); + fprintf (output,"SonyMode=%i\n", (int)SonyMode); + fprintf (output,"QisdaMode=%i\n", (int)QisdaMode); + fprintf (output,"QuantaMode=%i\n", (int)QuantaMode); + fprintf (output,"GCTMode=%i\n", (int)GCTMode); + fprintf (output,"KobilMode=%i\n", (int)KobilMode); + fprintf (output,"SequansMode=%i\n", (int)SequansMode); + fprintf (output,"MobileActionMode=%i\n", (int)MobileActionMode); + fprintf (output,"CiscoMode=%i\n", (int)CiscoMode); if ( MessageEndpoint ) - printf ("MessageEndpoint=0x%02x\n", MessageEndpoint); + fprintf (output,"MessageEndpoint=0x%02x\n", MessageEndpoint); else - printf ("MessageEndpoint= not set\n"); - printf ("MessageContent=\"%s\"\n", MessageContent); + fprintf (output,"MessageEndpoint= not set\n"); + fprintf (output,"MessageContent=\"%s\"\n", MessageContent); if ( strlen(MessageContent2) ) - printf ("MessageContent2=\"%s\"\n", MessageContent2); + fprintf (output,"MessageContent2=\"%s\"\n", MessageContent2); if ( strlen(MessageContent3) ) - printf ("MessageContent3=\"%s\"\n", MessageContent3); - printf ("NeedResponse=%i\n", (int)NeedResponse); + fprintf (output,"MessageContent3=\"%s\"\n", MessageContent3); + fprintf (output,"NeedResponse=%i\n", (int)NeedResponse); if ( ResponseEndpoint ) - printf ("ResponseEndpoint=0x%02x\n", ResponseEndpoint); + fprintf (output,"ResponseEndpoint=0x%02x\n", ResponseEndpoint); else - printf ("ResponseEndpoint= not set\n"); - printf ("Interface=0x%02x\n", Interface); + fprintf (output,"ResponseEndpoint= not set\n"); + if ( Interface > -1 ) + fprintf (output,"Interface=0x%02x\n", Interface); if ( Configuration > 0 ) - printf ("Configuration=0x%02x\n", Configuration); + fprintf (output,"Configuration=0x%02x\n", Configuration); if ( AltSetting > -1 ) - printf ("AltSetting=0x%02x\n", AltSetting); + fprintf (output,"AltSetting=0x%02x\n", AltSetting); if ( InquireDevice ) - printf ("\nInquireDevice enabled (default)\n"); + fprintf (output,"\nInquireDevice enabled (default)\n"); else - printf ("\nInquireDevice disabled\n"); + fprintf (output,"\nInquireDevice disabled\n"); if ( CheckSuccess ) - printf ("Success check enabled, max. wait time %d seconds\n", CheckSuccess); + fprintf (output,"Success check enabled, max. wait time %d seconds\n", CheckSuccess); else - printf ("Success check disabled\n"); + fprintf (output,"Success check disabled\n"); if ( sysmode ) - printf ("System integration mode enabled\n"); + fprintf (output,"System integration mode enabled\n"); else - printf ("System integration mode disabled\n"); - printf ("\n"); + fprintf (output,"System integration mode disabled\n"); + fprintf (output,"\n"); } int readArguments(int argc, char **argv) { int c, option_index = 0, count=0; + char *longConfig = NULL; if (argc==1) { printHelp(); @@ -383,7 +404,7 @@ int readArguments(int argc, char **argv) while (1) { - c = getopt_long (argc, argv, "heWQDndHSOGTNALRIv:p:V:P:C:m:M:2:3:w:r:c:i:u:a:s:", + c = getopt_long (argc, argv, "hejWQDndHSOBEGTNALRItv:p:V:P:C:m:M:2:3:w:r:c:i:u:a:s:f:b:g:", long_options, &option_index); /* Detect the end of the options. */ @@ -399,31 +420,46 @@ int readArguments(int argc, char **argv) case 'P': TargetProduct = strtol(optarg, NULL, 16); break; case 'C': TargetClass = strtol(optarg, NULL, 16); break; case 'm': MessageEndpoint = strtol(optarg, NULL, 16); break; - case 'M': strcpy(MessageContent, optarg); break; - case '2': strcpy(MessageContent2, optarg); break; - case '3': strcpy(MessageContent3, optarg); break; - case 'w': ReleaseDelay = strtol(optarg, NULL, 10); count--; break; + case 'M': strncpy(MessageContent, optarg, LINE_DIM); break; + case '2': strncpy(MessageContent2, optarg, LINE_DIM); break; + case '3': strncpy(MessageContent3, optarg, LINE_DIM); break; + case 'w': ReleaseDelay = strtol(optarg, NULL, 10); break; case 'n': NeedResponse = 1; break; case 'r': ResponseEndpoint = strtol(optarg, NULL, 16); break; case 'd': DetachStorageOnly = 1; break; case 'H': HuaweiMode = 1; break; case 'S': SierraMode = 1; break; case 'O': SonyMode = 1; break; + case 'B': QisdaMode = 1; break; + case 'E': QuantaMode = 1; break; case 'G': GCTMode = 1; break; case 'T': KobilMode = 1; break; case 'N': SequansMode = 1; break; case 'A': MobileActionMode = 1; break; case 'L': CiscoMode = 1; break; case 'c': readConfigFile(optarg); break; + case 't': readConfigFile("stdin"); break; case 'W': verbose = 1; show_progress = 1; count--; break; case 'Q': show_progress = 0; verbose = 0; count--; break; - case 'D': sysmode = 1; count--; break; + case 'D': sysmode = 1; InquireDevice = 0; count--; break; case 's': CheckSuccess = strtol(optarg, NULL, 10); count--; break; case 'I': InquireDevice = 0; break; + case 'b': busnum = strtol(optarg, NULL, 10); break; + case 'g': devnum = strtol(optarg, NULL, 10); break; case 'i': Interface = strtol(optarg, NULL, 16); break; case 'u': Configuration = strtol(optarg, NULL, 16); break; case 'a': AltSetting = strtol(optarg, NULL, 16); break; + case 'j': mbim = 1; break; + + case 'f': + longConfig = malloc(strlen(optarg)+5); + strcpy(longConfig,"##\n"); + strcat(longConfig,optarg); + strcat(longConfig,"\n"); + readConfigFile(longConfig); + free(longConfig); + break; case 'e': printVersion(); @@ -436,7 +472,7 @@ int readArguments(int argc, char **argv) break; default: /* Unsupported - error message has already been printed */ - printf ("\n"); + fprintf (output,"\n"); printHelp(); exit(1); } @@ -450,6 +486,9 @@ int main(int argc, char **argv) { int numDefaults=0, specialMode=0, sonySuccess=0; int currentConfig=0, defaultClass=0, interfaceClass=0; + struct libusb_device_descriptor descriptor; + struct libusb_config_descriptor *config; + /* Make sure we have empty strings even if not set by config */ TargetProductList[0] = '\0'; @@ -457,8 +496,12 @@ int main(int argc, char **argv) MessageContent2[0] = '\0'; MessageContent3[0] = '\0'; + /* Useful for debugging during boot */ +// output=fopen("/dev/console", "w"); + output=stdout; signal(SIGTERM, release_usb_device); + /* * Parameter parsing, USB preparation/diagnosis, plausibility checks */ @@ -469,24 +512,18 @@ int main(int argc, char **argv) break; default: /* one or more arguments except -W, -q or -s */ if (!config_read) /* if arguments contain -c, the config file was already processed */ - if (verbose) printf("Taking all parameters from the command line\n\n"); + if (verbose) fprintf(output,"Taking all parameters from the command line\n\n"); } - if (verbose) + if (verbose) { printVersion(); - - if (verbose) printConfig(); + SHOW_PROGRESS(output,"\n"); + } - /* libusb initialization */ - libusb_init(&ctx); - - if (verbose) - usb_set_debug(15); - - /* Plausibility checks. The default IDs are mandatory */ + /* Some sanity checks. The default IDs are mandatory */ if (!(DefaultVendor && DefaultProduct)) { - SHOW_PROGRESS("No default vendor/product ID given. Aborting.\n\n"); + SHOW_PROGRESS(output,"No default vendor/product ID given. Aborting.\n\n"); exit(1); } if (strlen(MessageContent)) { @@ -499,63 +536,91 @@ int main(int argc, char **argv) exit(1); } } - SHOW_PROGRESS("\n"); + + if (devnum == -1) { + searchMode = SEARCH_DEFAULT; + } else { + SHOW_PROGRESS(output,"Use given bus/device number: %03d/%03d ...\n", busnum, devnum); + searchMode = SEARCH_BUSDEV; + } if (show_progress) if (CheckSuccess && !(TargetVendor || TargetProduct > -1 || TargetProductList[0] != '\0') && !TargetClass) printf("Note: target parameter missing; success check limited\n"); + /* libusb initialization */ + libusb_init(&ctx); + + if (verbose) + usb_set_debug(15); + + if (mbim) { + printf("%d\n", findMBIMConfig(DefaultVendor, DefaultProduct, searchMode) ); + exit(0); + } + /* Count existing target devices, remember for success check */ - if (TargetVendor || TargetClass) { - SHOW_PROGRESS("Looking for target devices ...\n"); + if ((TargetVendor || TargetClass) && searchMode != SEARCH_BUSDEV) { + SHOW_PROGRESS(output,"Looking for target devices ...\n"); search_devices(&targetDeviceCount, TargetVendor, TargetProduct, TargetProductList, TargetClass, 0, SEARCH_TARGET); if (targetDeviceCount) { - SHOW_PROGRESS(" Found devices in target mode or class (%d)\n", targetDeviceCount); + SHOW_PROGRESS(output," Found devices in target mode or class (%d)\n", targetDeviceCount); } else - SHOW_PROGRESS(" No devices in target mode or class found\n"); + SHOW_PROGRESS(output," No devices in target mode or class found\n"); } /* Count default devices, get the last one found */ - SHOW_PROGRESS("Looking for default devices ...\n"); - dev = search_devices(&numDefaults, DefaultVendor, DefaultProduct, "\0", TargetClass, Configuration, SEARCH_DEFAULT); + SHOW_PROGRESS(output,"Looking for default devices ...\n"); + dev = search_devices(&numDefaults, DefaultVendor, DefaultProduct, "\0", TargetClass, Configuration, searchMode); if (numDefaults) { - SHOW_PROGRESS(" Found devices in default mode, class or configuration (%d)\n", numDefaults); + SHOW_PROGRESS(output," Found device in default mode, class or configuration (%d)\n", numDefaults); } else { - SHOW_PROGRESS(" No devices in default mode found. Nothing to do. Bye.\n\n"); + SHOW_PROGRESS(output," No devices in default mode found. Nothing to do. Bye.\n\n"); exit(0); } - if (dev != NULL) { - devnum = libusb_get_device_address(dev); - busnum = libusb_get_bus_number(dev); - SHOW_PROGRESS("Accessing device %03d on bus %03d ...\n", devnum, busnum); - libusb_open(dev, &devh); - } else { - SHOW_PROGRESS(" No default device found. Is it connected? Bye.\n\n"); + if (dev == NULL) { + SHOW_PROGRESS(output," No bus/device match. Is device connected? Bye.\n\n"); exit(0); + } else { + if (devnum == -1) { + devnum = libusb_get_device_address(dev); + busnum = libusb_get_bus_number(dev); + SHOW_PROGRESS(output,"Accessing device %03d on bus %03d ...\n", devnum, busnum); + } + libusb_open(dev, &devh); + if (devh == NULL) { + SHOW_PROGRESS(output,"Error opening the device. Aborting.\n\n"); + exit(1); + } } /* Get current configuration of default device - * A configuration value of -1 denotes a quirky device which has - * trouble determining the current configuration. Just use the first - * branch (which may be incorrect) + * A configuration value of -1 helps with quirky devices which have + * trouble determining the current configuration. We are just using the + * current config branch then. + * This affects only single-configuration devices so it's no problem. + * The dispatcher is using this always if no change of configuration + * is required for switching */ if (Configuration > -1) currentConfig = get_current_configuration(devh); else { - SHOW_PROGRESS("Skipping the check for the current configuration\n"); + SHOW_PROGRESS(output,"Skipping the check for the current configuration\n"); currentConfig = 0; } - /* Get class of default device/interface */ - struct libusb_device_descriptor descriptor; libusb_get_device_descriptor(dev, &descriptor); defaultClass = descriptor.bDeviceClass; - struct libusb_config_descriptor *config; libusb_get_config_descriptor(dev, 0, &config); - interfaceClass = config->interface[0].altsetting[0].bInterfaceClass; + if (Interface == -1) + Interface = config->interface[0].altsetting[0].bInterfaceNumber; + SHOW_PROGRESS(output,"Using interface number %d\n", Interface); + + /* Get class of default device/interface */ + interfaceClass = get_interface_class(config, Interface); libusb_free_config_descriptor(config); if (interfaceClass == -1) { - fprintf(stderr, "Error: getting the interface class failed. Aborting.\n\n"); + fprintf(stderr, "Error: getting the class of interface %d failed. Does it exist? Aborting.\n\n",Interface); exit(1); } @@ -564,10 +629,17 @@ int main(int argc, char **argv) else if (interfaceClass == 8 && defaultClass != 8) { /* Weird device with default class other than 0 and differing interface class */ - SHOW_PROGRESS("Ambiguous Class/InterfaceClass: 0x%02x/0x08\n", defaultClass); + SHOW_PROGRESS(output,"Ambiguous Class/InterfaceClass: 0x%02x/0x08\n", defaultClass); defaultClass = 8; } + if (strlen(MessageContent) && strncmp("55534243",MessageContent,8) == 0) + if (defaultClass != 8) { + fprintf(stderr, "Error: can't use storage command in MessageContent with interface %d;\n" + " interface class is %d, should be 8. Aborting.\n\n", Interface, defaultClass); + exit(1); + } + /* Check or get endpoints */ if (strlen(MessageContent) || InquireDevice || CiscoMode) { if (!MessageEndpoint) @@ -582,23 +654,23 @@ int main(int argc, char **argv) fprintf(stderr,"Error: response endpoint not given or found. Aborting.\n\n"); exit(1); } - SHOW_PROGRESS("Using endpoints 0x%02x (out) and 0x%02x (in)\n", MessageEndpoint, ResponseEndpoint); + SHOW_PROGRESS(output,"Using endpoints 0x%02x (out) and 0x%02x (in)\n", MessageEndpoint, ResponseEndpoint); } if (!MessageEndpoint || !ResponseEndpoint) if (InquireDevice && defaultClass == 0x08) { - SHOW_PROGRESS("Endpoints not found, skipping SCSI inquiry\n"); + SHOW_PROGRESS(output,"Endpoints not found, skipping SCSI inquiry\n"); InquireDevice = 0; } if (InquireDevice && show_progress) { if (defaultClass == 0x08) { - SHOW_PROGRESS("Inquiring device details; driver will be detached ...\n"); + SHOW_PROGRESS(output,"Inquiring device details; driver will be detached ...\n"); detachDriver(); if (deviceInquire() >= 0) InquireDevice = 2; } else - SHOW_PROGRESS("Not a storage device, skipping SCSI inquiry\n"); + SHOW_PROGRESS(output,"Not a storage device, skipping SCSI inquiry\n"); } deviceDescription(); @@ -612,15 +684,15 @@ int main(int argc, char **argv) } /* Some scenarios are exclusive, so check for unwanted combinations */ - specialMode = DetachStorageOnly + HuaweiMode + SierraMode + SonyMode + KobilMode - + SequansMode + MobileActionMode + CiscoMode; + specialMode = DetachStorageOnly + HuaweiMode + SierraMode + SonyMode + QisdaMode + KobilMode + + SequansMode + MobileActionMode + CiscoMode + QuantaMode; if ( specialMode > 1 ) { - SHOW_PROGRESS("Invalid mode combination. Check your configuration. Aborting.\n\n"); + SHOW_PROGRESS(output,"Invalid mode combination. Check your configuration. Aborting.\n\n"); exit(1); } if ( !specialMode && !strlen(MessageContent) && AltSetting == -1 && Configuration == 0 ) - SHOW_PROGRESS("Warning: no switching method given.\n"); + SHOW_PROGRESS(output,"Warning: no switching method given.\n"); /* * The switching actions @@ -628,17 +700,17 @@ int main(int argc, char **argv) if (sysmode) { openlog("usb_modeswitch", 0, LOG_SYSLOG); - syslog(LOG_NOTICE, "switching %04x:%04x (%s: %s)", DefaultVendor, DefaultProduct, imanufact, iproduct); + syslog(LOG_NOTICE, "switching device %04x:%04x on %03d/%03d", DefaultVendor, DefaultProduct, busnum, devnum); } if (DetachStorageOnly) { - SHOW_PROGRESS("Only detaching storage driver for switching ...\n"); + SHOW_PROGRESS(output,"Only detaching storage driver for switching ...\n"); if (InquireDevice == 2) { - SHOW_PROGRESS(" Any driver was already detached for inquiry\n"); + SHOW_PROGRESS(output," Any driver was already detached for inquiry\n"); } else { ret = detachDriver(); if (ret == 2) - SHOW_PROGRESS(" You may want to remove the storage driver manually\n"); + SHOW_PROGRESS(output," You may want to remove the storage driver manually\n"); } } @@ -652,10 +724,16 @@ int main(int argc, char **argv) detachDriver(); switchGCTMode(); } + if (QisdaMode) { + switchQisdaMode(); + } if(KobilMode) { detachDriver(); switchKobilMode(); } + if(QuantaMode) { + switchQuantaMode(); + } if (SequansMode) { switchSequansMode(); } @@ -668,7 +746,7 @@ int main(int argc, char **argv) } if (SonyMode) { if (CheckSuccess) - SHOW_PROGRESS("Note: ignoring CheckSuccess. Separate checks for Sony mode\n"); + SHOW_PROGRESS(output,"Note: ignoring CheckSuccess. Separate checks for Sony mode\n"); CheckSuccess = 0; /* separate and implied success control */ sonySuccess = switchSonyMode(); } @@ -679,7 +757,7 @@ int main(int argc, char **argv) detachDriver(); switchSendMessage(); } else - SHOW_PROGRESS("Warning: ignoring MessageContent. Can't combine with special mode\n"); + SHOW_PROGRESS(output,"Warning: ignoring MessageContent. Can't combine with special mode\n"); } if (Configuration > 0) { @@ -687,13 +765,13 @@ int main(int argc, char **argv) if (switchConfiguration()) { currentConfig = get_current_configuration(devh); if (currentConfig == Configuration) { - SHOW_PROGRESS("The configuration was set successfully\n"); + SHOW_PROGRESS(output,"The configuration was set successfully\n"); } else { - SHOW_PROGRESS("Changing the configuration has failed\n"); + SHOW_PROGRESS(output,"Changing the configuration has failed\n"); } } } else { - SHOW_PROGRESS("Target configuration %d already active. Doing nothing\n", currentConfig); + SHOW_PROGRESS(output,"Target configuration %d already active. Doing nothing\n", currentConfig); } } @@ -714,6 +792,11 @@ int main(int argc, char **argv) } if (CheckSuccess) { + if (searchMode == SEARCH_BUSDEV && sysmode) { + SHOW_PROGRESS(output,"Bus/dev search active, referring success check to wrapper. Bye.\n\n"); + printf("ok:busdev\n"); + goto CLOSING; + } if (checkSuccess()) { if (sysmode) { if (NoDriverLoading) @@ -734,16 +817,16 @@ int main(int argc, char **argv) syslog(LOG_NOTICE, "switched S.E. MD400 to modem mode"); printf("ok:\n"); /* ACM device, no driver action */ } - SHOW_PROGRESS("-> device should be stable now. Bye.\n\n"); + SHOW_PROGRESS(output,"-> device should be stable now. Bye.\n\n"); } else { if (sysmode) printf("fail:\n"); - SHOW_PROGRESS("-> switching was probably not completed. Bye.\n\n"); + SHOW_PROGRESS(output,"-> switching was probably not completed. Bye.\n\n"); } else - SHOW_PROGRESS("-> Run lsusb to note any changes. Bye.\n\n"); + SHOW_PROGRESS(output,"-> Run lsusb to note any changes. Bye.\n\n"); } - +CLOSING: if (sysmode) closelog(); if (devh) @@ -823,20 +906,20 @@ int deviceInquire () ret = usb_claim_interface(devh, Interface); if (ret != 0) { - SHOW_PROGRESS(" Could not claim interface (error %d). Skipping device inquiry\n", ret); + SHOW_PROGRESS(output," Could not claim interface (error %d). Skipping device inquiry\n", ret); goto out; } usb_clear_halt(devh, MessageEndpoint); ret = usb_bulk_write(devh, MessageEndpoint, (char *)command, 31, 0); if (ret < 0) { - SHOW_PROGRESS(" Could not send INQUIRY message (error %d)\n", ret); + SHOW_PROGRESS(output," Could not send INQUIRY message (error %d)\n", ret); goto out; } ret = usb_bulk_read(devh, ResponseEndpoint, data, 36, 0); if (ret < 0) { - SHOW_PROGRESS(" Could not get INQUIRY response (error %d)\n", ret); + SHOW_PROGRESS(output," Could not get INQUIRY response (error %d)\n", ret); goto out; } @@ -867,6 +950,65 @@ out: } +int findMBIMConfig(int vendor, int product, int mode) +{ + struct libusb_device **devs; + int resultConfig=0; + int i=0, j; + + if (libusb_get_device_list(ctx, &devs) < 0) { + perror("failed to access USB"); + return 0; + } + + SHOW_PROGRESS(output,"Search USB devices...\n"); + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor descriptor; + libusb_get_device_descriptor(dev, &descriptor); + + if (mode == SEARCH_BUSDEV) { + if ((libusb_get_bus_number(dev) != busnum) || + (libusb_get_device_address(dev) != devnum)) { + continue; + } else { +// if (verbose) +// fprintf (output," searching devices, found USB ID %04x:%04x\n", descriptor.idVendor, descriptor.idProduct); + if (descriptor.idVendor != vendor) + continue; + if (product != descriptor.idProduct) + continue; + } + } + SHOW_PROGRESS(output,"Found device, searching for MBIM configuration...\n"); + + // No check if there is only one configuration + if (descriptor.bNumConfigurations < 2) + return -1; + + // Checking all interfaces of all configurations + for (j=0; jbConfigurationValue; + for (i=0; ibNumInterfaces; i++) { +// SHOW_PROGRESS(output,"MBIM Check: looking at ifc %d, class is %d, subclass is %d\n", +// i,config->interface[i].altsetting[0].bInterfaceClass,config->interface[i].altsetting[0].bInterfaceSubClass); + + if ( config->interface[i].altsetting[0].bInterfaceClass == 2 ) + if ( config->interface[i].altsetting[0].bInterfaceSubClass == 0x0e ) { + // found MBIM interface in this configuration + libusb_free_config_descriptor(config); + return resultConfig; + } + } + libusb_free_config_descriptor(config); + } + return -1; + } + return 0; +} + void resetUSB () { int success; @@ -890,9 +1032,9 @@ void resetUSB () } while (success < 0); if ( success ) { - SHOW_PROGRESS("\n Reset failed. Can be ignored if device switched OK.\n"); + SHOW_PROGRESS(output,"\n Reset failed. Can be ignored if device switched OK.\n"); } else - SHOW_PROGRESS("\n OK, device was reset\n"); + SHOW_PROGRESS(output,"\n OK, device was reset\n"); } @@ -909,16 +1051,16 @@ int switchSendMessage () // if (MessageContent2[0] != '\0' || MessageContent3[0] != '\0') // NeedResponse = 1; - SHOW_PROGRESS("Setting up communication with interface %d\n", Interface); + SHOW_PROGRESS(output,"Setting up communication with interface %d\n", Interface); if (InquireDevice != 2) { ret = usb_claim_interface(devh, Interface); if (ret != 0) { - SHOW_PROGRESS(" Could not claim interface (error %d). Skipping message sending\n", ret); + SHOW_PROGRESS(output," Could not claim interface (error %d). Skipping message sending\n", ret); return 0; } } usb_clear_halt(devh, MessageEndpoint); - SHOW_PROGRESS("Using endpoint 0x%02x for message sending ...\n", MessageEndpoint); + SHOW_PROGRESS(output,"Using endpoint 0x%02x for message sending ...\n", MessageEndpoint); if (show_progress) fflush(stdout); @@ -932,11 +1074,11 @@ int switchSendMessage () if (NeedResponse) { if ( strstr(msg[i],cmdHead) != NULL ) { // UFI command - SHOW_PROGRESS("Reading the response to message %d (CSW) ...\n", i+1); + SHOW_PROGRESS(output,"Reading the response to message %d (CSW) ...\n", i+1); ret = read_bulk(ResponseEndpoint, ByteString, 13); } else { // Other bulk transfer - SHOW_PROGRESS("Reading the response to message %d ...\n", i+1); + SHOW_PROGRESS(output,"Reading the response to message %d ...\n", i+1); ret = read_bulk(ResponseEndpoint, ByteString, strlen(msg[i])/2 ); } if (ret < 0) @@ -944,17 +1086,18 @@ int switchSendMessage () } } - SHOW_PROGRESS("Resetting response endpoint 0x%02x\n", ResponseEndpoint); + SHOW_PROGRESS(output,"Resetting response endpoint 0x%02x\n", ResponseEndpoint); ret = usb_clear_halt(devh, ResponseEndpoint); if (ret) - SHOW_PROGRESS(" Could not reset endpoint (probably harmless): %d\n", ret); - SHOW_PROGRESS("Resetting message endpoint 0x%02x\n", MessageEndpoint); + SHOW_PROGRESS(output," Could not reset endpoint (probably harmless): %d\n", ret); + SHOW_PROGRESS(output,"Resetting message endpoint 0x%02x\n", MessageEndpoint); ret = usb_clear_halt(devh, MessageEndpoint); if (ret) - SHOW_PROGRESS(" Could not reset endpoint (probably harmless): %d\n", ret); - usleep(200000); + SHOW_PROGRESS(output," Could not reset endpoint (probably harmless): %d\n", ret); + usleep(50000); + if (ReleaseDelay) { - SHOW_PROGRESS("Blocking the interface for %d ms before releasing ...\n", ReleaseDelay); + SHOW_PROGRESS(output,"Blocking the interface for %d ms before releasing ...\n", ReleaseDelay); usleep(ReleaseDelay*1000); } ret = usb_release_interface(devh, Interface); @@ -963,29 +1106,27 @@ int switchSendMessage () return 1; skip: - SHOW_PROGRESS(" Device is gone, skipping any further commands\n"); + SHOW_PROGRESS(output," Device is gone, skipping any further commands\n"); usb_close(devh); devh = 0; return 2; } -#define SWITCH_CONFIG_MAXTRIES 5 - int switchConfiguration () { int count = SWITCH_CONFIG_MAXTRIES; int ret; - SHOW_PROGRESS("Changing configuration to %i ...\n", Configuration); - while (((ret = usb_set_configuration(devh, Configuration)) < 0) && --count) { - SHOW_PROGRESS(" Device is busy, trying to detach kernel driver\n"); - detachDriver(); - } + SHOW_PROGRESS(output,"Changing configuration to %i ...\n", Configuration); + while (((ret = usb_set_configuration(devh, Configuration)) < 0) && --count) { + SHOW_PROGRESS(output," Device is busy, trying to detach kernel driver\n"); + detachDriver(); + } if (ret == 0 ) { - SHOW_PROGRESS(" OK, configuration set\n"); + SHOW_PROGRESS(output," OK, configuration set\n"); return 1; } - SHOW_PROGRESS(" Setting the configuration returned error %d. Trying to continue\n", ret); + SHOW_PROGRESS(output," Setting the configuration returned error %d. Trying to continue\n", ret); return 0; } @@ -994,15 +1135,15 @@ int switchAltSetting () { int ret; - SHOW_PROGRESS("Changing to alt setting %i ...\n", AltSetting); + SHOW_PROGRESS(output,"Changing to alt setting %i ...\n", AltSetting); ret = usb_claim_interface(devh, Interface); ret = libusb_set_interface_alt_setting(devh, Interface, AltSetting); usb_release_interface(devh, Interface); if (ret != 0) { - SHOW_PROGRESS(" Changing to alt setting returned error %d. Trying to continue\n", ret); + SHOW_PROGRESS(output," Changing to alt setting returned error %d. Trying to continue\n", ret); return 0; } else { - SHOW_PROGRESS(" OK, changed to alt setting\n"); + SHOW_PROGRESS(output," OK, changed to alt setting\n"); return 1; } } @@ -1012,13 +1153,13 @@ void switchHuaweiMode () { int ret; - SHOW_PROGRESS("Sending Huawei control message ...\n"); + SHOW_PROGRESS(output,"Sending Huawei control message ...\n"); ret = usb_control_msg(devh, USB_TYPE_STANDARD | USB_RECIP_DEVICE, USB_REQ_SET_FEATURE, 00000001, 0, buffer, 0, 1000); if (ret != 0) { fprintf(stderr, "Error: sending Huawei control message failed (error %d). Aborting.\n\n", ret); exit(1); } else - SHOW_PROGRESS(" OK, Huawei control message sent\n"); + SHOW_PROGRESS(output," OK, Huawei control message sent\n"); } @@ -1026,13 +1167,17 @@ void switchSierraMode () { int ret; - SHOW_PROGRESS("Trying to send Sierra control message\n"); - ret = usb_control_msg(devh, 0x40, 0x0b, 00000001, 0, buffer, 0, 1000); + SHOW_PROGRESS(output,"Trying to send Sierra control message\n"); + ret = usb_control_msg(devh, USB_TYPE_VENDOR, 0x0b, 00000001, 0, buffer, 0, 1000); + if (ret == LIBUSB_ERROR_PIPE) { + SHOW_PROGRESS(output," communication with device stopped. May have switched, continue ...\n"); + return; + } if (ret != 0) { fprintf(stderr, "Error: sending Sierra control message failed (error %d). Aborting.\n\n", ret); exit(1); } else - SHOW_PROGRESS(" OK, Sierra control message sent\n"); + SHOW_PROGRESS(output," OK, Sierra control message sent\n"); } @@ -1042,15 +1187,15 @@ void switchGCTMode () ret = usb_claim_interface(devh, Interface); if (ret != 0) { - SHOW_PROGRESS(" Could not claim interface (error %d). Skipping GCT sequence \n", ret); + SHOW_PROGRESS(output," Could not claim interface (error %d). Skipping GCT sequence \n", ret); return; } - SHOW_PROGRESS("Sending GCT control message 1 ...\n"); + SHOW_PROGRESS(output,"Sending GCT control message 1 ...\n"); ret = usb_control_msg(devh, 0xa1, 0xa0, 0, Interface, buffer, 1, 1000); - SHOW_PROGRESS("Sending GCT control message 2 ...\n"); + SHOW_PROGRESS(output,"Sending GCT control message 2 ...\n"); ret = usb_control_msg(devh, 0xa1, 0xfe, 0, Interface, buffer, 1, 1000); - SHOW_PROGRESS(" OK, GCT control messages sent\n"); + SHOW_PROGRESS(output," OK, GCT control messages sent\n"); usb_release_interface(devh, Interface); } @@ -1058,16 +1203,41 @@ void switchGCTMode () int switchKobilMode() { int ret; - SHOW_PROGRESS("Sending Kobil control message ...\n"); + SHOW_PROGRESS(output,"Sending Kobil control message ...\n"); ret = usb_control_msg(devh, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, 0x88, 0, 0, buffer, 8, 1000); if (ret != 0) { fprintf(stderr, "Error: sending Kobil control message failed (error %d). Aborting.\n\n", ret); exit(1); } else - SHOW_PROGRESS(" OK, Kobil control message sent\n"); + SHOW_PROGRESS(output," OK, Kobil control message sent\n"); return 1; } +void switchQuantaMode() { + int ret; + + SHOW_PROGRESS(output,"Sending Quanta control message ...\n"); + ret = usb_control_msg(devh, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, 0xff, 0, 0, buffer, 0, 1000); + if (ret < 0) { + SHOW_PROGRESS(output," Sending Quanta control message returned error %d, continue anyway ...\n", ret); + } else { + SHOW_PROGRESS(output," OK, Quanta control message sent\n"); + } +} + +int switchQisdaMode () { + int ret; + + SHOW_PROGRESS(output,"Sending Qisda control message ...\n"); + memcpy(buffer, "\x05\x8c\x04\x08\xa0\xee\x20\x00\x5c\x01\x04\x08\x98\xcd\xea\xbf", 16); + ret = usb_control_msg(devh, 0x40, 0x04, 00000000, 0, buffer, 16, 1000); + if (ret != 0) { + fprintf(stderr, "Error: sending Qisda control message failed (error %d). Aborting.\n\n", ret); + exit(1); + } else + SHOW_PROGRESS(output," OK, Qisda control message sent\n"); + return 1; +} int switchSonyMode () { @@ -1079,13 +1249,13 @@ int switchSonyMode () CheckSuccess = 0; } - SHOW_PROGRESS("Trying to send Sony control message\n"); + SHOW_PROGRESS(output,"Trying to send Sony control message\n"); ret = usb_control_msg(devh, 0xc0, 0x11, 2, 0, buffer, 3, 100); if (ret < 0) { fprintf(stderr, "Error: sending Sony control message failed (error %d). Aborting.\n\n", ret); exit(1); } else - SHOW_PROGRESS(" OK, control message sent, waiting for device to return ...\n"); + SHOW_PROGRESS(output," OK, control message sent, waiting for device to return ...\n"); usb_close(devh); devh = 0; @@ -1108,27 +1278,27 @@ int switchSonyMode () } i++; } - SHOW_PROGRESS("\n After %d seconds:",i); + SHOW_PROGRESS(output,"\n After %d seconds:",i); if ( dev ) { - SHOW_PROGRESS(" device came back, proceeding\n"); + SHOW_PROGRESS(output," device came back, proceeding\n"); libusb_open(dev, &devh); if (devh == 0) { fprintf(stderr, "Error: could not get handle on device\n"); return 0; } } else { - SHOW_PROGRESS(" device still gone, cancelling\n"); + SHOW_PROGRESS(output," device still gone, cancelling\n"); return 0; } sleep(1); - SHOW_PROGRESS("Sending Sony control message again ...\n"); + SHOW_PROGRESS(output,"Sending Sony control message again ...\n"); ret = usb_control_msg(devh, 0xc0, 0x11, 2, 0, buffer, 3, 100); if (ret < 0) { fprintf(stderr, "Error: sending Sony control message (2) failed (error %d)\n", ret); return 0; } - SHOW_PROGRESS(" OK, control message sent\n"); + SHOW_PROGRESS(output," OK, control message sent\n"); return 1; } @@ -1143,7 +1313,7 @@ int switchSonyMode () int switchActionMode () { int i; - SHOW_PROGRESS("Sending MobileAction control sequence ...\n"); + SHOW_PROGRESS(output,"Sending MobileAction control sequence ...\n"); memcpy(buffer, "\xb0\x04\x00\x00\x02\x90\x26\x86", SIZE); usb_control_msg(devh, USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x0300, 0, buffer, SIZE, 1000); memcpy(buffer, "\xb0\x04\x00\x00\x02\x90\x26\x86", SIZE); @@ -1179,10 +1349,10 @@ int switchActionMode () ret = usb_interrupt_write(devh, EP_OUT, buffer, SIZE, 1000); usb_interrupt_read(devh, EP_IN, buffer, SIZE, 1000); if (ret < 0) { - SHOW_PROGRESS(" MobileAction control sequence did not complete\n Last error was %d\n",ret); + SHOW_PROGRESS(output," MobileAction control sequence did not complete\n Last error was %d\n",ret); return 1; } else { - SHOW_PROGRESS(" MobileAction control sequence complete\n"); + SHOW_PROGRESS(output," MobileAction control sequence complete\n"); return 0; } } @@ -1198,13 +1368,13 @@ int switchActionMode () int switchSequansMode() { int ret; - SHOW_PROGRESS("Sending Sequans vendor request\n"); + SHOW_PROGRESS(output,"Sending Sequans vendor request\n"); ret = usb_control_msg(devh, USB_TYPE_VENDOR | USB_RECIP_DEVICE, SQN_SET_DEVICE_MODE_REQUEST, SQN_CUSTOM_DEVICE_MODE, 0, buffer, 0, 1000); if (ret != 0) { fprintf(stderr, "Error: sending Sequans request failed (error %d). Aborting.\n\n", ret); exit(1); } else - SHOW_PROGRESS(" OK, Sequans request was sent\n"); + SHOW_PROGRESS(output," OK, Sequans request was sent\n"); return 1; } @@ -1213,7 +1383,7 @@ int switchCiscoMode() { int ret, i; char* msg[11]; - SHOW_PROGRESS("Preparing for sending Cisco message sequence\n"); + SHOW_PROGRESS(output,"Preparing for sending Cisco message sequence\n"); msg[0] = "55534243f83bcd810002000080000afd000000030000000100000000000000"; msg[1] = "55534243984300820002000080000afd000000070000000100000000000000"; @@ -1227,28 +1397,28 @@ int switchCiscoMode() { msg[9] = "55534243d84c04820000000000000afd000300241300000000000000000000"; msg[10] = "55534243d84c04820000000000000afd000110732400000000000000000000"; - SHOW_PROGRESS("Setting up communication with interface %d\n", Interface); + SHOW_PROGRESS(output,"Setting up communication with interface %d\n", Interface); ret = usb_claim_interface(devh, Interface); if (ret != 0) { - SHOW_PROGRESS(" Could not claim interface (error %d). Skipping message sending\n", ret); + SHOW_PROGRESS(output," Could not claim interface (error %d). Skipping message sending\n", ret); return 0; } // usb_clear_halt(devh, MessageEndpoint); if (show_progress) - fflush(stdout); + fflush(output); for (i=0; i<11; i++) { if ( sendMessage(msg[i], i+1) ) goto skip; - SHOW_PROGRESS("Reading the response (CSW) to bulk message %d ...\n",i+1); + SHOW_PROGRESS(output,"Reading the response (CSW) to bulk message %d ...\n",i+1); ret = read_bulk(ResponseEndpoint, ByteString, 13); if (ret < 0) goto skip; } if (ReleaseDelay) { - SHOW_PROGRESS("Blocking the interface for %d ms before releasing ...\n", ReleaseDelay); + SHOW_PROGRESS(output,"Blocking the interface for %d ms before releasing ...\n", ReleaseDelay); usleep(ReleaseDelay*1000); } ret = usb_release_interface(devh, Interface); @@ -1257,7 +1427,7 @@ int switchCiscoMode() { return 1; skip: - SHOW_PROGRESS("Device returned error, skipping any further commands\n"); + SHOW_PROGRESS(output,"Device returned error, skipping any further commands\n"); usb_close(devh); devh = 0; return 2; @@ -1274,28 +1444,30 @@ int detachDriver() return 2; #else - SHOW_PROGRESS("Looking for active driver ...\n"); + SHOW_PROGRESS(output,"Looking for active driver ...\n"); ret = usb_get_driver_np(devh, Interface, buffer, BUF_SIZE); if (ret != 0) { - SHOW_PROGRESS(" No driver found. Either detached before or never attached\n"); + SHOW_PROGRESS(output," No driver found. Either detached before or never attached\n"); return 1; } - SHOW_PROGRESS(" OK, driver found (\"%s\")\n", buffer); - if (DetachStorageOnly && strcmp(buffer,"usb-storage")) { - SHOW_PROGRESS(" Warning: driver is not usb-storage\n"); + if (strncmp("dummy",buffer,5) == 0) { + SHOW_PROGRESS(output," OK, driver found; name unknown, limitation of libusb1\n"); + strcpy(buffer,"unkown"); + } else { + SHOW_PROGRESS(output," OK, driver found (\"%s\")\n", buffer); } #endif #ifndef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP - SHOW_PROGRESS(" Can't do driver detaching on this platform\n"); + SHOW_PROGRESS(output," Can't do driver detaching on this platform\n"); return 2; #else ret = usb_detach_kernel_driver_np(devh, Interface); if (ret == 0) { - SHOW_PROGRESS(" OK, driver \"%s\" detached\n", buffer); + SHOW_PROGRESS(output," OK, driver \"%s\" detached\n", buffer); } else - SHOW_PROGRESS(" Driver \"%s\" detach failed with error %d. Trying to continue\n", buffer, ret); + SHOW_PROGRESS(output," Driver \"%s\" detach failed with error %d. Trying to continue\n", buffer, ret); return 1; #endif } @@ -1314,10 +1486,10 @@ int sendMessage(char* message, int count) fprintf(stderr, "Error: MessageContent %d %s\n is not a hex string. Skipping ...\n", count, MessageContent); return 1; } - SHOW_PROGRESS("Trying to send message %d to endpoint 0x%02x ...\n", count, MessageEndpoint); - fflush(stdout); + SHOW_PROGRESS(output,"Trying to send message %d to endpoint 0x%02x ...\n", count, MessageEndpoint); + fflush(output); ret = write_bulk(MessageEndpoint, ByteString, message_length); - if (ret == -19) + if (ret == LIBUSB_ERROR_NO_DEVICE) return 1; return 0; @@ -1329,7 +1501,7 @@ int checkSuccess() int i=0, ret; int newTargetCount, success=0; - SHOW_PROGRESS("\nChecking for mode switch (max. %d times, once per second) ...\n", CheckSuccess); + SHOW_PROGRESS(output,"\nChecking for mode switch (max. %d times, once per second) ...\n", CheckSuccess); sleep(1); /* If target parameters are given, don't check for vanished device @@ -1357,18 +1529,18 @@ int checkSuccess() /* Test if default device still can be accessed; positive result does * not necessarily mean failure */ - SHOW_PROGRESS(" Waiting for original device to vanish ...\n"); + SHOW_PROGRESS(output," Waiting for original device to vanish ...\n"); ret = usb_claim_interface(devh, Interface); usb_release_interface(devh, Interface); if (ret < 0) { - SHOW_PROGRESS(" Original device can't be accessed anymore. Good.\n"); + SHOW_PROGRESS(output," Original device can't be accessed anymore. Good.\n"); usb_close(devh); devh = 0; break; } if (i == CheckSuccess-1) { - SHOW_PROGRESS(" Original device still present after the timeout\n\nMode switch most likely failed. Bye.\n\n"); + SHOW_PROGRESS(output," Original device still present after the timeout\n\nMode switch most likely failed. Bye.\n\n"); } else sleep(1); } @@ -1380,7 +1552,7 @@ int checkSuccess() * description is read for syslog message */ for (i=i; i < CheckSuccess; i++) { - SHOW_PROGRESS(" Searching for target devices ...\n"); + SHOW_PROGRESS(output," Searching for target devices ...\n"); dev = search_devices(&newTargetCount, TargetVendor, TargetProduct, TargetProductList, TargetClass, 0, SEARCH_TARGET); if (dev && (newTargetCount > targetDeviceCount)) { printf("\nFound target device, now opening\n"); @@ -1398,20 +1570,20 @@ int checkSuccess() printf(" Serial No.: %s\n", iserial); printf("-------------------------\n"); } - SHOW_PROGRESS(" Found correct target device\n\nMode switch succeeded. Bye.\n\n"); + SHOW_PROGRESS(output," Found correct target device\n\nMode switch succeeded. Bye.\n\n"); success = 2; break; } if (i == CheckSuccess-1) { - SHOW_PROGRESS(" No new devices in target mode or class found\n\nMode switch has failed. Bye.\n\n"); + SHOW_PROGRESS(output," No new devices in target mode or class found\n\nMode switch has failed. Bye.\n\n"); } else sleep(1); } } else /* No target data given, rely on the vanished device */ if (!devh) { - SHOW_PROGRESS(" (For a better success check provide target IDs or class)\n"); - SHOW_PROGRESS(" Original device vanished after switching\n\nMode switch most likely succeeded. Bye.\n\n"); + SHOW_PROGRESS(output," (For a better success check provide target IDs or class)\n"); + SHOW_PROGRESS(output," Original device vanished after switching\n\nMode switch most likely succeeded. Bye.\n\n"); success = 1; } @@ -1424,7 +1596,7 @@ int checkSuccess() break; case 2: if (sysmode) - syslog(LOG_NOTICE, "switched to %04x:%04x (%s: %s)", TargetVendor, TargetProduct, imanufact, iproduct); + syslog(LOG_NOTICE, "switched to %04x:%04x on %03d/%03d", TargetVendor, TargetProduct, busnum, devnum); success = 1; break; case 1: @@ -1446,12 +1618,12 @@ int write_bulk(int endpoint, char *message, int length) int ret; ret = usb_bulk_write(devh, endpoint, message, length, 3000); if (ret >= 0 ) { - SHOW_PROGRESS(" OK, message successfully sent\n"); + SHOW_PROGRESS(output," OK, message successfully sent\n"); } else - if (ret == -19) { - SHOW_PROGRESS(" Device seems to have vanished right after sending. Good.\n"); + if (ret == LIBUSB_ERROR_NO_DEVICE) { + SHOW_PROGRESS(output," Device seems to have vanished right after sending. Good.\n"); } else - SHOW_PROGRESS(" Sending the message returned error %d. Trying to continue\n", ret); + SHOW_PROGRESS(output," Sending the message returned error %d. Trying to continue\n", ret); return ret; } @@ -1462,18 +1634,18 @@ int read_bulk(int endpoint, char *buffer, int length) ret = usb_bulk_read(devh, endpoint, buffer, length, 3000); usb_bulk_read(devh, endpoint, buffer, 13, 100); if (ret >= 0 ) { - SHOW_PROGRESS(" OK, response successfully read (%d bytes).\n", ret); + SHOW_PROGRESS(output," OK, response successfully read (%d bytes).\n", ret); } else - if (ret == -19) { - SHOW_PROGRESS(" Device seems to have vanished after reading. Good.\n"); + if (ret == LIBUSB_ERROR_NO_DEVICE) { + SHOW_PROGRESS(output," Device seems to have vanished after reading. Good.\n"); } else - SHOW_PROGRESS(" Response reading got error %d\n", ret); + SHOW_PROGRESS(output," Response reading got error %d\n", ret); return ret; } -void release_usb_device(int dummy) { - SHOW_PROGRESS("Program cancelled by system. Bye.\n\n"); +void release_usb_device(int __attribute__((unused)) dummy) { + SHOW_PROGRESS(output,"Program cancelled by system. Bye.\n\n"); if (devh) { usb_release_interface(devh, Interface); usb_close(devh); @@ -1485,8 +1657,8 @@ void release_usb_device(int dummy) { } -/* Iterates over busses and devices, counts the ones with the given - * ID/class and returns the last one of them +/* Iterates over busses and devices, counts the ones which match the given + * parameters and returns the last one of them */ struct libusb_device* search_devices( int *numFound, int vendor, int product, char* productList, int targetClass, int configuration, int mode) { @@ -1494,6 +1666,8 @@ struct libusb_device* search_devices( int *numFound, int vendor, int product, ch int devClass; struct libusb_device* right_dev = NULL; struct libusb_device_handle *testdevh; + struct libusb_device **devs; + int i=0; /* only target class given, target vendor and product assumed unchanged */ if ( targetClass && !(vendor || product) ) { @@ -1509,37 +1683,43 @@ struct libusb_device* search_devices( int *numFound, int vendor, int product, ch if (productList != '\0') listcopy = malloc(strlen(productList)+1); - struct libusb_device **devs; - int i=0; - if (libusb_get_device_list(ctx, &devs) < 0) { - perror ("failed to access USB"); + perror("failed to access USB"); return 0; } while ((dev = devs[i++]) != NULL) { - struct libusb_device_descriptor descriptor; - libusb_get_device_descriptor(dev, &descriptor); - int idVendor = descriptor.idVendor; - int idProduct = descriptor.idProduct; + struct libusb_device_descriptor descriptor; + libusb_get_device_descriptor(dev, &descriptor); + int idVendor = descriptor.idVendor; + int idProduct = descriptor.idProduct; + + if (mode == SEARCH_BUSDEV) { + if ((libusb_get_bus_number(dev) != busnum) || + (libusb_get_device_address(dev) != devnum)) + continue; + else + SHOW_PROGRESS(output," bus/device number matched\n"); + } if (verbose) - printf (" searching devices, found USB ID %04x:%04x\n", idVendor, idProduct); + fprintf (output," searching devices, found USB ID %04x:%04x\n", + idVendor, idProduct); if (idVendor != vendor) continue; if (verbose) - printf (" found matching vendor ID\n"); + fprintf (output," found matching vendor ID\n"); // product list given if ( strlen(productList) ) { strcpy(listcopy, productList); token = strtok(listcopy, ","); while (token != NULL) { if (strlen(token) != 4) { - SHOW_PROGRESS("Error: entry in product ID list has wrong length: %s. Ignoring\n", token); + SHOW_PROGRESS(output,"Error: entry in product ID list has wrong length: %s. Ignoring\n", token); goto NextToken; } if ( hexstr2bin(token, buffer, strlen(token)/2) == -1) { - SHOW_PROGRESS("Error: entry in product ID list is not a hex string: %s. Ignoring\n", token); + SHOW_PROGRESS(output,"Error: entry in product ID list is not a hex string: %s. Ignoring\n", token); goto NextToken; } product = 0; @@ -1548,7 +1728,7 @@ struct libusb_device* search_devices( int *numFound, int vendor, int product, ch product += (unsigned char)buffer[1]; if (product == idProduct) { if (verbose) - printf (" found matching product ID from list\n"); + fprintf (output," found matching product ID from list\n"); (*numFound)++; if (busnum == -1) right_dev = dev; @@ -1559,20 +1739,17 @@ struct libusb_device* search_devices( int *numFound, int vendor, int product, ch break; } } - NextToken: token = strtok(NULL, ","); } /* Product ID is given */ } else if (product == idProduct) { - if (verbose) - printf (" found matching product ID\n"); + SHOW_PROGRESS(output," found matching product ID\n"); if (targetClass == 0 && configuration < 1) { (*numFound)++; + SHOW_PROGRESS(output," adding device\n"); right_dev = dev; - if (verbose) - printf (" adding device\n"); } else { if (targetClass != 0) { struct libusb_device_descriptor descriptor; @@ -1591,23 +1768,23 @@ struct libusb_device* search_devices( int *numFound, int vendor, int product, ch devClass = ifaceClass; if (devClass == targetClass) { if (verbose) - printf (" target class %02x matching\n", targetClass); + fprintf (output," target class %02x matching\n", targetClass); if (mode == SEARCH_TARGET) { (*numFound)++; right_dev = dev; if (verbose) - printf (" adding device\n"); + fprintf (output," adding device\n"); } else if (verbose) - printf (" not adding device\n"); + fprintf (output," not adding device\n"); } else { if (verbose) - printf (" target class %02x not matching\n", targetClass); - if (mode == SEARCH_DEFAULT) { + fprintf (output," target class %02x not matching\n", targetClass); + if (mode == SEARCH_DEFAULT || mode == SEARCH_BUSDEV) { (*numFound)++; right_dev = dev; if (verbose) - printf (" adding device\n"); + fprintf (output," adding device\n"); } } } else { @@ -1616,28 +1793,19 @@ struct libusb_device* search_devices( int *numFound, int vendor, int product, ch int testconfig = get_current_configuration(testdevh); if (testconfig != configuration) { if (verbose) - printf (" device configuration %d not matching parameter\n", testconfig); + fprintf (output," device configuration %d not matching parameter\n", testconfig); (*numFound)++; right_dev = dev; if (verbose) - printf (" adding device\n"); + fprintf (output," adding device\n"); } else if (verbose) - printf (" not adding device, target configuration already set\n"); + fprintf (output," not adding device, target configuration already set\n"); } } - /* hack: if busnum has other than init value, we are called from - * successCheck() and do probe for plausible new devnum/busnum - */ - if (busnum != -1) - if (libusb_get_device_address(dev) < devnum || libusb_get_bus_number(dev) != busnum) { - if (verbose) - printf (" warning: busnum/devnum indicates an unrelated device\n"); - //right_dev = NULL; - } } } - if (productList != NULL) + if (listcopy != NULL) free(listcopy); return right_dev; } @@ -1692,7 +1860,7 @@ int get_current_configuration(struct libusb_device_handle* devh) { int ret; - SHOW_PROGRESS("Getting the current device configuration ...\n"); + SHOW_PROGRESS(output,"Getting the current device configuration ...\n"); ret = usb_control_msg(devh, USB_DIR_IN + USB_TYPE_STANDARD + USB_RECIP_DEVICE, USB_REQ_GET_CONFIGURATION, 0, 0, buffer, 1, 1000); if (ret < 0) { // There are quirky devices which fail to respond properly to this command @@ -1703,45 +1871,102 @@ int get_current_configuration(struct libusb_device_handle* devh) } return 1; } else { - SHOW_PROGRESS(" OK, got current device configuration (%d)\n", buffer[0]); + SHOW_PROGRESS(output," OK, got current device configuration (%d)\n", buffer[0]); return buffer[0]; } } +int get_interface_class(struct libusb_config_descriptor *cfg, int ifcNumber) +{ + int i; + + for (i=0; ibNumInterfaces; i++) { +// SHOW_PROGRESS(output,"Test: looking at ifc %d, class is %d\n",i,cfg->interface[i].altsetting[0].bInterfaceClass); + if (cfg->interface[i].altsetting[0].bInterfaceNumber == ifcNumber) + return cfg->interface[i].altsetting[0].bInterfaceClass; + } + + return -1; +} /* Parameter parsing */ char* ReadParseParam(const char* FileName, char *VariableName) { - static char Str[LINE_DIM]; + static int numLines = 0; + static char* ConfigBuffer[MAXLINES]; + char *VarName, *Comment=NULL, *Equal=NULL; char *FirstQuote, *LastQuote, *P1, *P2; - int Line=0, Len=0, Pos=0; - FILE *file=fopen(FileName, "r"); - - if (file==NULL) { - fprintf(stderr, "Error: Could not find file %s\n\n", FileName); - exit(1); + int Line=0; + unsigned Len=0, Pos=0; + char Str[LINE_DIM], *token, *configPos; + FILE *file = NULL; + + // Reading and storing input during the first call + if (numLines==0) { + if (strncmp(FileName,"##",2) == 0) { + if (verbose) fprintf(output,"\nReading long config from command line\n"); + // "Embedded" configuration data + configPos = (char*)FileName; + token = strtok(configPos, "\n"); + strncpy(Str,token,LINE_DIM-1); + } else { + if (strcmp(FileName, "stdin")==0) { + if (verbose) fprintf(output,"\nReading long config from stdin\n"); + file = stdin; + } else { + if (verbose) fprintf(output,"\nReading config file: %s\n", FileName); + file=fopen(FileName, "r"); + } + if (file==NULL) { + fprintf(stderr, "Error: Could not find file %s\n\n", FileName); + exit(1); + } else { + token = fgets(Str, LINE_DIM-1, file); + } + } + while (token != NULL && numLines < MAXLINES) { +// Line++; + Len=strlen(Str); + if (Len==0) + goto NextLine; + if (Str[Len-1]=='\n' or Str[Len-1]=='\r') + Str[--Len]='\0'; + Equal = strchr (Str, '='); // search for equal sign + Pos = strcspn (Str, ";#!"); // search for comment + Comment = (Pos==Len) ? NULL : Str+Pos; + if (Equal==NULL or ( Comment!=NULL and Comment<=Equal)) + goto NextLine; // Comment or irrelevant, don't save + Len=strlen(Str)+1; + ConfigBuffer[numLines] = malloc(Len*sizeof(char)); + strcpy(ConfigBuffer[numLines],Str); + numLines++; + NextLine: + if (file == NULL) { + token = strtok(NULL, "\n"); + if (token != NULL) + strncpy(Str,token,LINE_DIM-1); + } else + token = fgets(Str, LINE_DIM-1, file); + } + if (file != NULL) + fclose(file); } - while (fgets(Str, LINE_DIM-1, file) != NULL) { - Line++; - Len=strlen(Str); - if (Len==0) goto Next; - if (Str[Len-1]=='\n' or Str[Len-1]=='\r') Str[--Len]='\0'; + // Now checking for parameters + Line=0; + while (Line < numLines) { + strcpy(Str,ConfigBuffer[Line]); Equal = strchr (Str, '='); // search for equal sign - Pos = strcspn (Str, ";#!"); // search for comment - Comment = (Pos==Len) ? NULL : Str+Pos; - if (Equal==NULL or ( Comment!=NULL and Comment<=Equal)) goto Next; // Only comment *Equal++ = '\0'; - if (Comment!=NULL) *Comment='\0'; // String FirstQuote=strchr (Equal, '"'); // search for double quote char LastQuote=strrchr (Equal, '"'); if (FirstQuote!=NULL) { if (LastQuote==NULL) { - fprintf(stderr, "Error reading parameter file %s line %d - Missing end quote.\n", FileName, Line); + fprintf(stderr, "Error reading parameters from file %s - Missing end quote:\n%s\n", FileName, Str); goto Next; } *FirstQuote=*LastQuote='\0'; @@ -1751,7 +1976,7 @@ char* ReadParseParam(const char* FileName, char *VariableName) // removes leading/trailing spaces Pos=strspn (Str, " \t"); if (Pos==strlen(Str)) { - fprintf(stderr, "Error reading parameter file %s line %d - Missing variable name.\n", FileName, Line); + fprintf(stderr, "Error reading parameters from file %s - Missing variable name:\n%s\n", FileName, Str); goto Next; // No function name } while ((P1=strrchr(Str, ' '))!=NULL or (P2=strrchr(Str, '\t'))!=NULL) @@ -1761,19 +1986,18 @@ char* ReadParseParam(const char* FileName, char *VariableName) Pos=strspn (Equal, " \t"); if (Pos==strlen(Equal)) { - fprintf(stderr, "Error reading parameter file %s line %d - Missing value.\n", FileName, Line); + fprintf(stderr, "Error reading parameter from file %s - Missing value:\n%s\n", FileName, Str); goto Next; // No function name } Equal+=Pos; if (strcmp(VarName, VariableName)==0) { // Found it - fclose(file); return Equal; } - Next:; + Next: + Line++; } - fclose(file); return NULL; } @@ -1822,44 +2046,51 @@ int hexstr2bin(const char *hex, char *buffer, int len) void printVersion() { char* version = VERSION; - printf("\n * usb_modeswitch: handle USB devices with multiple modes\n"); - printf(" * Version %s (C) Josua Dietze 2011\n", version); - printf(" * Based on libusb0 (0.1.12 and above)\n\n"); - printf(" ! PLEASE REPORT NEW CONFIGURATIONS !\n\n"); + printf("\n * usb_modeswitch: handle USB devices with multiple modes\n" + " * Version %s (C) Josua Dietze 2012\n" + " * Based on libusb0 (0.1.12 and above)\n\n" + " ! PLEASE REPORT NEW CONFIGURATIONS !\n\n", version); } void printHelp() { - printf ("\nUsage: usb_modeswitch [-hvpVPmMrndHSOGATRIQWDiua] [-c filename]\n\n"); - printf (" -h, --help this help\n"); - printf (" -e, --version print version information and exit\n"); - printf (" -v, --default-vendor NUM vendor ID of original mode (mandatory)\n"); - printf (" -p, --default-product NUM product ID of original mode (mandatory)\n"); - printf (" -V, --target-vendor NUM target mode vendor ID (optional)\n"); - printf (" -P, --target-product NUM target mode product ID (optional)\n"); - printf (" -C, --target-class NUM target mode device class (optional)\n"); - printf (" -m, --message-endpoint NUM direct the message transfer there (optional)\n"); - printf (" -M, --message-content message to send (hex number as string)\n"); - printf (" -2 , -3 additional messages to send (-n recommended)\n"); - printf (" -n, --need-response read response to the message transfer (CSW)\n"); - printf (" -r, --response-endpoint NUM read response from there (optional)\n"); - printf (" -d, --detach-only detach the active driver, no further action\n"); - printf (" -H, --huawei-mode apply a special procedure\n"); - printf (" -S, --sierra-mode apply a special procedure\n"); - printf (" -O, --sony-mode apply a special procedure\n"); - printf (" -G, --gct-mode apply a special procedure\n"); - printf (" -N, --sequans-mode apply a special procedure\n"); - printf (" -A, --mobileaction-mode apply a special procedure\n"); - printf (" -T, --kobil-mode apply a special procedure\n"); - printf (" -L, --cisco-mode apply a special procedure\n"); - printf (" -R, --reset-usb reset the device after all other actions\n"); - printf (" -Q, --quiet don't show progress or error messages\n"); - printf (" -W, --verbose print all settings and debug output\n"); - printf (" -D, --sysmode specific result and syslog message\n"); - printf (" -s, --success switching result check with timeout\n"); - printf (" -I, --no-inquire do not get SCSI attributes (default on)\n\n"); - printf (" -c, --config-file load configuration from file\n\n"); - printf (" -i, --interface NUM select initial USB interface (default 0)\n"); - printf (" -u, --configuration NUM select USB configuration\n"); - printf (" -a, --altsetting NUM select alternative USB interface setting\n\n"); + fprintf (output,"\nUsage: usb_modeswitch [] [-c filename]\n\n" + " -h, --help this help\n" + " -e, --version print version information and exit\n" + " -j, --find-mbim return config no. with MBIM interface, exit\n" + " -v, --default-vendor NUM vendor ID of original mode (mandatory)\n" + " -p, --default-product NUM product ID of original mode (mandatory)\n" + " -V, --target-vendor NUM target mode vendor ID (optional)\n" + " -P, --target-product NUM target mode product ID (optional)\n" + " -C, --target-class NUM target mode device class (optional)\n" + " -b, --busnum NUM system bus number of device (for hard ID)\n" + " -g, --devnum NUM system device number (for hard ID)\n" + " -m, --message-endpoint NUM direct the message transfer there (optional)\n" + " -M, --message-content message to send (hex number as string)\n" + " -2 , -3 additional messages to send (-n recommended)\n" + " -n, --need-response read response to the message transfer (CSW)\n" + " -r, --response-endpoint NUM read response from there (optional)\n" + " -d, --detach-only detach the active driver, no further action\n" + " -H, --huawei-mode apply a special procedure\n" + " -S, --sierra-mode apply a special procedure\n" + " -O, --sony-mode apply a special procedure\n" + " -G, --gct-mode apply a special procedure\n" + " -N, --sequans-mode apply a special procedure\n" + " -A, --mobileaction-mode apply a special procedure\n" + " -T, --kobil-mode apply a special procedure\n" + " -L, --cisco-mode apply a special procedure\n" + " -B, --qisda-mode apply a special procedure\n" + " -E, --quanta-mode apply a special procedure\n" + " -R, --reset-usb reset the device after all other actions\n" + " -Q, --quiet don't show progress or error messages\n" + " -W, --verbose print all settings and debug output\n" + " -D, --sysmode specific result and syslog message\n" + " -s, --success switching result check with timeout\n" + " -I, --no-inquire do not get SCSI attributes (default on)\n\n" + " -c, --config-file load long configuration from file\n\n" + " -t, --stdinput read long configuration from stdin\n\n" + " -f, --long-config get long configuration from string\n\n" + " -i, --interface NUM select initial USB interface (default 0)\n" + " -u, --configuration NUM select USB configuration\n" + " -a, --altsetting NUM select alternative USB interface setting\n\n"); } diff --git a/release/src/router/usbmodeswitch/usb_modeswitch.conf b/release/src/router/usbmodeswitch/usb_modeswitch.conf index d7ab78e5fd..55aa71b75a 100644 --- a/release/src/router/usbmodeswitch/usb_modeswitch.conf +++ b/release/src/router/usbmodeswitch/usb_modeswitch.conf @@ -1,8 +1,8 @@ # Configuration for the usb_modeswitch package, a mode switching tool for # USB devices providing multiple states or modes # -# This file is evaluated by the wrapper script "usb_modeswitch_dispatcher" -# in /usr/sbin +# Evaluated by the wrapper script /usr/sbin/usb_modeswitch_dispatcher +# # To enable an option, set it to "1", "yes" or "true" (case doesn't matter) # Everything else counts as "disable" @@ -14,6 +14,14 @@ DisableSwitching=0 # Enable logging (results in a extensive report file in /var/log, named -# "usb_modeswitch_" (and probably others) +# "usb_modeswitch_" and probably others EnableLogging=0 + + +# Optional increase of "delay_use" for the usb-storage driver; there are hints +# that a recent kernel default change to 1 sec. may lead to problems, particu- +# larly with USB 3.0 ports. Set this to at least 3 (seconds) in that case. +# Does nothing if the current system value is same or higher + +# SetStorageDelay=3 diff --git a/release/src/router/usbmodeswitch/usb_modeswitch.h b/release/src/router/usbmodeswitch/usb_modeswitch.h index c17f68aad8..b0363f5c7e 100644 --- a/release/src/router/usbmodeswitch/usb_modeswitch.h +++ b/release/src/router/usbmodeswitch/usb_modeswitch.h @@ -2,8 +2,8 @@ This file is part of usb_modeswitch, a mode switching tool for controlling flip flop (multiple device) USB gear - Version 1.1.9, 2011/08/05 - Copyright (C) 2007 - 2011 Josua Dietze + Version 1.2.6, 2013/06/02 + Copyright (C) 2007 - 2013 Josua Dietze Config file parsing stuff borrowed from Guillaume Dargaud (http://www.gdargaud.net/Hack/SourceCode.html) @@ -35,6 +35,8 @@ void switchHuaweiMode(); void switchSierraMode(); void switchGCTMode(); int switchKobilMode(); +int switchQisdaMode(); +void switchQuantaMode(); int switchSequansMode(); int switchActionMode(); int switchSonyMode(); @@ -50,7 +52,7 @@ struct libusb_device* search_devices( int *numFound, int vendor, int product, ch int find_first_bulk_output_endpoint(struct libusb_device *dev); int find_first_bulk_input_endpoint(struct libusb_device *dev); int get_current_configuration(struct libusb_device_handle* devh); -int get_interface0_class(struct libusb_device *dev, int devconfig); +int get_interface_class(struct libusb_config_descriptor *cfg, int ifcNumber); char* ReadParseParam(const char* FileName, char *VariableName); int hex2num(char c); int hex2byte(const char *hex); @@ -62,6 +64,8 @@ void deviceDescription(); int deviceInquire(); void resetUSB(); void release_usb_device(int dummy); +int findMBIMConfig(int vendor, int product, int mode); + // Boolean #define and && diff --git a/release/src/router/usbmodeswitch/usb_modeswitch.sh b/release/src/router/usbmodeswitch/usb_modeswitch.sh index fad4855521..8e9ba74df3 100755 --- a/release/src/router/usbmodeswitch/usb_modeswitch.sh +++ b/release/src/router/usbmodeswitch/usb_modeswitch.sh @@ -1,5 +1,5 @@ #!/bin/sh -# part of usb_modeswitch 1.1.9 +# part of usb_modeswitch 1.2.6 device_in() { if [ ! -e /var/lib/usb_modeswitch/$1 ]; then @@ -37,10 +37,12 @@ if [ $(expr "$1" : "--.*") ]; then v_id=$3 fi fi +PATH=/sbin:/usr/sbin:$PATH case "$1" in --driver-bind) ( dir=$(ls -d /sys$2/ttyUSB* 2>/dev/null) + sleep 1 if [ ! -z "$dir" ]; then exit 0 fi @@ -49,13 +51,13 @@ case "$1" in if [ "$?" = "1" ]; then id_attr="/sys/bus/usb-serial/drivers/option1/new_id" if [ ! -e "$id_attr" ]; then - /sbin/modprobe option 2>/dev/null || true + modprobe option 2>/dev/null || true fi if [ -e "$id_attr" ]; then - echo "$v_id $p_id" > $id_attr + echo "$v_id $p_id ff" > $id_attr else - /sbin/modprobe -r usbserial - /sbin/modprobe usbserial "vendor=0x$v_id" "product=0x$p_id" + modprobe -r usbserial + modprobe usbserial "vendor=0x$v_id" "product=0x$p_id" fi fi ) & @@ -64,8 +66,8 @@ case "$1" in --symlink-name) device_in "link_list" $v_id $p_id if [ "$?" = "1" ]; then - if [ -e "/usr/bin/tclsh" ]; then - exec /usr/bin/tclsh /usr/sbin/usb_modeswitch_dispatcher $1 $2 $v_id $p_id 2>/dev/null + if [ -e "/usr/sbin/usb_modeswitch_dispatcher" ]; then + exec usb_modeswitch_dispatcher $1 $2 2>>/dev/null fi fi exit 0 @@ -75,11 +77,11 @@ exec 1<&- 2<&- 5<&- 7<&- ( count=120 while [ $count != 0 ]; do - if [ ! -e "/usr/bin/tclsh" ]; then + if [ ! -e "/usr/sbin/usb_modeswitch_dispatcher" ]; then sleep 1 count=$(($count - 1)) else - exec /usr/bin/tclsh /usr/sbin/usb_modeswitch_dispatcher "$@" 2>/dev/null & + exec usb_modeswitch_dispatcher --switch-mode $1 $0 & exit 0 fi done diff --git a/release/src/router/usbmodeswitch/usb_modeswitch.tcl b/release/src/router/usbmodeswitch/usb_modeswitch.tcl index b9c8d30b9c..7f8c37d2d6 100755 --- a/release/src/router/usbmodeswitch/usb_modeswitch.tcl +++ b/release/src/router/usbmodeswitch/usb_modeswitch.tcl @@ -9,92 +9,104 @@ # the mode switching program with the matching parameter # file from /usr/share/usb_modeswitch # -# Part of usb-modeswitch-1.1.9 package -# (C) Josua Dietze 2009, 2010, 2011 - +# Part of usb-modeswitch-1.2.6 package +# (C) Josua Dietze 2009-2013 + +set arg0 [lindex $argv 0] +if [regexp {\.tcl$} $arg0] { + if [file exists $arg0] { + set argv [lrange $argv 1 end] + source $arg0 + exit + } +} -# Setting of the these switches is done in the global config +# Setting of these switches is done in the global config # file (/etc/usb_modeswitch.conf) if available -set logging 0 -set noswitching 0 - - -set env(PATH) "/bin:/usr/bin" +set flags(logging) 0 +set flags(noswitching) 0 +set flags(stordelay) 0 # Execution starts at file bottom -proc {Main} {argc argv} { +proc {Main} {argv argc} { -global scsi usb config match wc device logging noswitching settings +global scsi usb config match device flags setup devdir set loginit [ParseGlobalConfig] # The facility to add a symbolic link pointing to the # ttyUSB port which provides interrupt transfer, i.e. -# the port to connect through; returns a symlink name -# for udev and exits -# This is run once for every known device interface by +# the port to connect through. +# Will check for interrupt endpoint in ttyUSB port (lowest if +# there is more than one); if found, return "gsmmodem[n]" name +# to udev for symlink creation + +# This is run once for every port of LISTED devices by # an udev rule if {[lindex $argv 0] == "--symlink-name"} { - if $logging { - set device [clock clicks] - } puts -nonewline [SymLinkName [lindex $argv 1]] SafeExit } -set settings(dbdir) /usr/share/usb_modeswitch -set settings(dbdir_etc) /etc/usb_modeswitch.d - -if {![file exists $settings(dbdir)] && ![file exists $settings(dbdir_etc)]} { - set device "noname" - Log "Error: no config database found in /usr/share or /etc. Exiting" - SafeExit -} -set bindir /usr/sbin - -set devList1 {} -set devList2 {} - -# argv contains the values provided from the udev rule -# separated by "/" - -set argList [split [lindex $argv 0] /] - +set argList [split [lindex $argv 1] /] if [string length [lindex $argList 1]] { set device [lindex $argList 1] } else { set device "noname" } -Log "Raw args from udev: $argv\n\n$loginit" +if {$flags(stordelay) > 0} { + SetStorageDelay $flags(stordelay) +} +Log "Raw args from udev: [lindex $argv 1]\n\n$loginit" if {$device == "noname"} { Log "No data from udev. Exiting" SafeExit } +if {[lindex $argv 0] != "--switch-mode"} { + Log "No command given. Exiting" + SafeExit +} + +set setup(dbdir) /usr/share/usb_modeswitch +set setup(dbdir_etc) /etc/usb_modeswitch.d + + +if {![file exists $setup(dbdir)] && ![file exists $setup(dbdir_etc)]} { + Log "Error: no config database found in /usr/share or /etc. Exiting" + SafeExit +} +set bindir /usr/sbin + +set devList1 {} +set devList2 {} + + # arg 0: the bus id for the device (udev: %b) # arg 1: the "kernel name" for the device (udev: %k) # -# Both together give the top directory where the path -# to the SCSI attributes can be determined (further down) -# Addendum: older kernel/udev version seem to differ in -# providing these attributes - or not. So more probing -# is needed +# Used to determine the top directory for the device in sysfs +set ifChk 0 if {[string length [lindex $argList 0]] == 0} { if {[string length [lindex $argList 1]] == 0} { Log "No device number values given from udev! Exiting" SafeExit } else { - Log "Bus ID for device not given by udev." - Log " Trying to determine it from kernel name ([lindex $argList 1]) ..." if {![regexp {(.*?):} [lindex $argList 1] d dev_top]} { - Log "Could not determine top device dir from udev values! Exiting" - SafeExit + if [regexp {([0-9]+-[0-9]+\.?[0-9]*.*)} [lindex $argList 1] d dev_top] { + # new udev rules file, got to check class of first interface + Log "Called by new rules file - remember to check class of first interface ..." + set ifChk 1 + } else { + Log "Could not determine device dir from udev values! Exiting" + SafeExit + } } } } else { @@ -102,12 +114,16 @@ if {[string length [lindex $argList 0]] == 0} { regexp {(.*?):} $dev_top d dev_top } +# NC +#set dev_top [lindex $argList 1] +# set devdir /sys/bus/usb/devices/$dev_top if {![file isdirectory $devdir]} { - Log "Top sysfs directory not found ($devdir)! Exiting" + Log "Top device directory not found ($devdir)! Exiting" SafeExit } +Log "Using top device dir $devdir" # Mapping of the short string identifiers (in the config @@ -124,23 +140,51 @@ set match(uSe) usb(serial) # Now reading the USB attributes - -ReadUSBAttrs $devdir - -if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} { - Log "USB IDs not found in sysfs tree. Exiting" +if {![ReadUSBAttrs $devdir]} { + Log "USB attributes not found in sysfs tree. Exiting" SafeExit } -Log "----------------\nUSB values from sysfs:" -foreach attr {manufacturer product serial} { - Log " $attr\t$usb($attr)" +set iface 0 +if $ifChk { + Log "Check class of first interface ..." + set iface [ChkIface 0] + if {$iface < 0} { + set iface [ChkIface 9] + } + if {$iface < 0} { + if {$usb(idVendor)=="19d2" && $usb(idProduct)=="2000"} { + set iface [ChkIface 3] + } +# Corrected, was wrongly reported +# if {$usb(idVendor)=="16d8" && $usb(idProduct)=="6803"} { +# set iface [ChkIface 3] +# } + } + if {$iface < 0} { + Log " Device is not in install mode. Exiting" + SafeExit + } else { + Log " Device is in install mode." + } +} +set ifdir [file tail [IfDir $iface]] +regexp {:([0-9]+\.[0-9]+)$} $ifdir d iface + +Log "Using interface $iface" + + +if $flags(logging) { + Log "----------------\nUSB values from sysfs:" + foreach attr {manufacturer product serial} { + Log " $attr\t$usb($attr)" + } + Log "----------------" } -Log "----------------" -if $noswitching { +if $flags(noswitching) { Log "\nSwitching globally disabled. Exiting\n" - catch {exec logger -p syslog.notice "usb_modeswitch: switching disabled, no action for $usb(idVendor):$usb(idProduct)" 2>/dev/null} + SysLog "usb_modeswitch: switching disabled, no action for $usb(idVendor):$usb(idProduct)" SafeExit } @@ -168,107 +212,25 @@ if {[llength $configList] > 1} { set scsiNeeded 1 } } -if {!$scsiNeeded} { - Log "SCSI attributes not needed, moving on" -} - - -# Getting the SCSI values via libusb results in a detached -# usb-storage driver. Not good for devices that want to be -# left alone. Fortunately, the sysfs tree provides the values -# too without need for direct access - -# First we wait until the SCSI data is ready - or timeout. -# Timeout means: no storage driver was bound to the device. -# We run 20 times max, every half second (max. 10 seconds -# total) - -# We also check if the device itself changes, probably -# because it was switched by the kernel (or even unplugged). -# Then we do simply nothing and exit quietly ... - -set counter 0 -while {$scsiNeeded && $counter < 20} { - after 500 - incr counter - Log "waiting for storage tree in sysfs" - - set sysdir $devdir/[lindex $argList 1] - - if {![file isdirectory $sysdir]} { - # Device is gone. Unplugged? Switched by kernel? - Log "sysfs device tree is gone; exiting" - SafeExit - } - set rc [open $devdir/product r] - set newproduct [read -nonewline $rc] - close $rc - if {![string match $newproduct $usb(product)]} { - # Device has just changed. Switched by someone else? - Log "device has changed; exiting" - SafeExit - } - - # Searching the storage/SCSI tree; might take a while - if {[set dirList [glob -nocomplain $sysdir/host*]] != ""} { - set sysdir [lindex $dirList 0] - if {[set dirList [glob -nocomplain $sysdir/target*]] != ""} { - set sysdir [lindex $dirList 0] - regexp {.*target(.*)} $sysdir d subdir - if {[set dirList [glob -nocomplain $sysdir/$subdir*]] != ""} { - set sysdir [lindex $dirList 0] - if [file exists $sysdir/vendor] { - # Finally SCSI structure is ready, get the values - ReadSCSIAttrs $sysdir - Log "SCSI values read" - break - } - } - } - } -} if $scsiNeeded { - if {$counter == 20 && [string length $scsi(vendor)] == 0} { - Log "SCSI tree not found; you may want to check if this path/file exists:" - Log "$sysdir/vendor\n" - } else { + if [ReadSCSIAttrs $devdir:$iface] { Log "----------------\nSCSI values from sysfs:" foreach attr {vendor model rev} { Log " $attr\t$scsi($attr)" } Log "----------------" + } else { + Log "Could not get SCSI attributes, exclude devices with SCSI match" } - Log "Waiting 3 secs. after SCSI device was added" - after 3000 } else { -# after 500 + Log "SCSI attributes not needed, moving on" } -# If SCSI tree in sysfs was not identified, try and get the values -# from a (nonswitching) call of usb_modeswitch; this detaches the -# storage driver, so it's just the last resort +# General wait - this is important +after 500 -if {$scsiNeeded && $scsi(vendor)==""} { - set testSCSI [exec $bindir/usb_modeswitch -v 0x$usb(idVendor) -p 0x$usb(idProduct) 2>/dev/null] - regexp { Vendor String: (.*?)\n} $testSCSI d scsi(vendor) - regexp { Model String: (.*?)\n} $testSCSI d scsi(model) - regexp {Revision String: (.*?)\n} $testSCSI d scsi(rev) - Log "SCSI values from usb_modeswitch:" - foreach attr {vendor model rev} { - Log " $attr\t$scsi($attr)" - } -} - -# If we don't have the SCSI values by now, we just -# leave the variables empty; they won't match anything - -# Time to check for a matching config file. -# Matching itself is done by MatchDevice -# -# The configuration file names are sorted reverse so that -# the ones with matching additions are tried first; the -# common configs without match attributes are used at the -# end and provide a fallback +# Now check for a matching config file. Matching is done +# by MatchDevice set report {} foreach configuration $configList { @@ -278,32 +240,57 @@ foreach configuration $configList { Log "checking config: $configuration" if [MatchDevice $configuration] { - ParseDeviceConfig [ConfigGet conffile $configuration] - set devList1 [ListSerialDevs] + Log "! matched. Reading config data" + if [string length $usb(busnum)] { + set busParam "-b [string trimleft $usb(busnum) 0]" + set devParam "-g [string trimleft $usb(devnum) 0]" + } else { + set busParam "" + set devParam "" + } + set configBuffer [ConfigGet conffile $configuration] + ParseDeviceConfig $configBuffer if {$config(waitBefore) == ""} { - Log "! matched, now switching" } else { - Log "! matched, waiting time set to $config(waitBefore) seconds" + Log " waiting time set to $config(waitBefore) seconds" append config(waitBefore) "000" after $config(waitBefore) Log " waiting is over, switching starts now" } + if {$config(noMBIMCheck)==0 && $usb(bNumConfigurations) > 1} { + Log "Device may have an MBIM configuration, checking driver ..." + if [CheckMBIM] { + Log " driver for MBIM devices is available" + Log "Finding MBIM configuration number ..." + if [catch {set cfgno [exec /usr/sbin/usb_modeswitch -j -Q $busParam $devParam -v $usb(idVendor) -p $usb(idProduct)]} err] { + Log "Error when trying to find MBIM configuration, switch to legacy modem mode" + } else { + set cfgno [string trim $cfgno] + if {$cfgno > 0} { + set config(Configuration) $cfgno + set config(driverModule) "" + set configBuffer "Configuration=$cfgno" + } else { + Log " No MBIM configuration found, switch to legacy modem mode" + } + } + } else { + Log " no MBIM driver found, switch to legacy modem mode" + } + } # Now we are actually switching - if $logging { - Log " (running command: $bindir/usb_modeswitch -I -W -c $settings(tmpConfig))" - set report [exec $bindir/usb_modeswitch -I -W -D -c $settings(tmpConfig) $configParam 2>@ stdout] + if $flags(logging) { + Log "Command to be run:\nusb_modeswitch -W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f \$configBuffer" + set report [exec /usr/sbin/usb_modeswitch -W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$configBuffer" 2>@ stdout] + Log "\nVerbose debug output of usb_modeswitch and libusb follows" + Log "(Note that some USB errors are to be expected in the process)" + Log "--------------------------------" + Log $report + Log "--------------------------------" + Log "(end of usb_modeswitch output)\n" } else { - set report [exec $bindir/usb_modeswitch -I -Q -D -c $settings(tmpConfig) $configParam 2>/dev/null] - } - Log "\nVerbose debug output of usb_modeswitch and libusb follows" - Log "(Note that some USB errors are expected in the process)" - Log "--------------------------------" - Log $report - Log "--------------------------------" - Log "(end of usb_modeswitch output)\n" - if [regexp {/var/lib/usb_modeswitch} $settings(tmpConfig)] { - file delete $settings(tmpConfig) + set report [exec /usr/sbin/usb_modeswitch -Q -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$configBuffer" 2>@ stdout] } break } else { @@ -311,95 +298,89 @@ foreach configuration $configList { } } -# We're finished with switching; success checking -# was done by usb_modeswitch and logged via syslog. -# -# If switching was OK we now check for drivers by -# simply recounting serial devices under /dev +# Switching is complete; success checking was either +# done by usb_modeswitch and logged via syslog OR bus/dev +# parameter were used; then we do check for success HERE -if {![file isdirectory $devdir]} { - Log "Device directory in sysfs is gone! Something went wrong, aborting" - SafeExit -} - # Give the device annother second if it's not fully back yet +if [regexp {ok:busdev} $report] { + if [CheckSuccess $devdir] { + Log "Mode switching was successful, found $usb(idVendor):$usb(idProduct) ($usb(manufacturer): $usb(product))" + SysLog "usb_modeswitch: switched to $usb(idVendor):$usb(idProduct) on [format %03d $usb(busnum)]/[format %03d $usb(devnum)]" + } else { + Log "\nTarget config not matching - current values are" + set attrList {idVendor idProduct bConfigurationValue manufacturer product serial} + foreach attr [lsort [array names usb]] { + Log " [format %-26s $attr:] $usb($attr)" + } + Log "\nMode switching may have failed. Exiting\n" + SafeExit + } +} else { + if {![file isdirectory $devdir]} { + Log "Device directory in sysfs is gone! Something went wrong, aborting" + SafeExit + } + if {![regexp {ok:} $report]} { + Log "\nCore program reported switching failure. Exiting\n" + SafeExit + } + # Give the device another second if it's not fully back yet if {![file exists $devdir/idProduct]} { after 1000 } -if {![file exists $devdir/idProduct]} { - after 1000 + ReadUSBAttrs $devdir $ifdir } -set ifdir "[file tail $devdir]:1.0" -ReadUSBAttrs $devdir $ifdir +# Now checking for bound drivers (only for class 0xff) -if {$usb($ifdir/bInterfaceClass) != "" && [regexp {ok:} $report]} { - if {$usb($ifdir/bInterfaceClass) == "02"} { - set report "ok:" - Log " Found CDC ACM device, skip driver checking" +if {$config(driverModule) != "" && $usb($ifdir/bInterfaceClass) != "" && [regexp {ok:} $report]} { + if {$usb($ifdir/bInterfaceClass) != "ff"} { + set config(driverModule) "" + Log " No vendor-specific class found, skip driver checking" } } -# If target ID given, driver shall be loaded -if [regexp -nocase {ok:[0-9a-f]{4}:[0-9a-f]{4}|ok:no_data} $report] { +# If module is set (it is by default), driver shall be loaded. +# If not, then NoDriverLoading is active +if {$config(driverModule) != ""} { if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} { - regexp {ok:(\w{4}):(\w{4})} $report d usb(idVendor) usb(idProduct) - } - set t "$usb(idVendor)$usb(idProduct)" - if {[string length $t] != 8 || [string trim $t 0] == ""} { - if {$report == "ok:no_data"} { - Log "Libusb1 bug prevented device searching, and device ID not found afterwards." - } - Log "No vendor/product ID found or given, can't continue. Aborting" - SafeExit - } - - # For general driver loading; TODO: add respective device names. - # Presently only useful for HSO devices (which are recounted now) - if {$config(driverModule) == ""} { - set config(driverModule) "option" - set config(driverIDPath) "/sys/bus/usb-serial/drivers/option1" - } else { - if {$config(driverIDPath) == ""} { - set config(driverIDPath) "/sys/bus/usb/drivers/$config(driverModule)" + if {![regexp {ok:(\w{4}):(\w{4})} $report d usb(idVendor) usb(idProduct)]} { + Log "No target vendor/product ID found or given, can't continue. Aborting" + SafeExit } } - Log "Driver module is \"$config(driverModule)\", ID path is $config(driverIDPath)\n" - - # some settling time in ms - after 500 - - Log "Now checking for newly created serial devices ..." - set devList2 [ListSerialDevs] - - if {[llength $devList1] >= [llength $devList2]} { - Log " no new serial devices found" + # wait for any drivers to bind automatically + after 1000 + Log "Now checking for bound driver ..." + if {![file exists $devdir/$ifdir/driver]} { + Log " no driver has bound to interface 0 yet" AddToList link_list $usb(idVendor):$usb(idProduct) # If device is known, the sh wrapper will take care, else: if {[InBindList $usb(idVendor):$usb(idProduct)] == 0} { - Log "Device not in bind_list" + Log "Device is not in \"bind_list\" yet, bind it now" # Load driver CheckDriverBind $usb(idVendor) $usb(idProduct) - set counter 0 # Old/slow systems may take a while to create the devices - while {[llength $devList1] >= [llength $devList2] && $counter < 14} { + set counter 0 + while {![file exists $devdir/$ifdir/driver]} { + if {$counter == 14} {break} after 500 - set devList2 [ListSerialDevs] incr counter } if {$counter == 14} { - Log " still no new serial devices found" + Log " driver binding failed" } else { - Log " driver successfully bound" + Log " driver was bound to the device" AddToList bind_list $usb(idVendor):$usb(idProduct) } } } else { - Log " new serial devices found, driver has bound" - if {[llength [lsearch -glob -all $devList2 *ttyUSB*]] > [llength [lsearch -glob -all $devList1 *ttyUSB*]]} { + Log " driver has bound, device is known" + if {[llength [glob -nocomplain $devdir/$ifdir/ttyUSB*]] > 0} { AddToList link_list $usb(idVendor):$usb(idProduct) } } @@ -417,7 +398,7 @@ if [regexp {ok:$} $report] { # reset (e.g. from usb-storage) which would possibly switch back # a mode-switching device to initial mode if [regexp {ok:} $report] { - Log "Checking for AVOID_RESET_QUIRK attribute" + Log "Checking for AVOID_RESET_QUIRK kernel attribute" if [file exists $devdir/avoid_reset_quirk] { if [catch {exec echo "1" >$devdir/avoid_reset_quirk 2>/dev/null} err] { Log " Error setting the attribute: $err" @@ -425,7 +406,7 @@ if [regexp {ok:} $report] { Log " AVOID_RESET_QUIRK activated" } } else { - Log " AVOID_RESET_QUIRK not present" + Log " not present in this kernel" } } @@ -436,14 +417,47 @@ SafeExit # end of proc {Main} -proc {ReadSCSIAttrs} {dir} { +proc {ReadSCSIAttrs} {topdir} { global scsi -Log "SCSI dir exists: $dir" +set counter 0 +set sysdir $topdir +Log "Checking storage tree in sysfs ..." +while {$counter < 20} { + Log " loop $counter/20" + if {![file isdirectory $sysdir]} { + # Device is gone. Unplugged? Switched by kernel? + Log " sysfs device tree is gone; abort SCSI value check" + return 0 + } + # Searching the storage/SCSI tree; might take a while + if {[set dirList [glob -nocomplain $topdir/host*]] != ""} { + set sysdir [lindex $dirList 0] + if {[set dirList [glob -nocomplain $sysdir/target*]] != ""} { + set sysdir [lindex $dirList 0] + regexp {.*target(.*)} $sysdir d subdir + if {[set dirList [glob -nocomplain $sysdir/$subdir*]] != ""} { + set sysdir [lindex $dirList 0] + if [file exists $sysdir/vendor] { + Log " Storage tree is ready" + break + } + } + } + } + after 500 + incr counter +} +if {$counter == 20} { + Log "SCSI tree not found; you may want to check if this path/file exists:" + Log "$sysdir/vendor\n" + return 0 +} +Log "Reading SCSI values ..." foreach attr {vendor model rev} { - if [file exists $dir/$attr] { - set rc [open $dir/$attr r] + if [file exists $sysdir/$attr] { + set rc [open $sysdir/$attr r] set scsi($attr) [read -nonewline $rc] close $rc } else { @@ -451,6 +465,7 @@ foreach attr {vendor model rev} { Log "Warning: SCSI attribute \"$attr\" not found." } } +return 1 } # end of proc {ReadSCSIAttrs} @@ -460,20 +475,28 @@ proc {ReadUSBAttrs} {dir args} { global usb -set attrList {idVendor idProduct manufacturer product serial bNumConfigurations} +set attrList {idVendor idProduct bConfigurationValue manufacturer product serial devnum busnum bNumConfigurations} +set mandatoryList {idVendor idProduct bNumConfigurations} +set result 1 if {$args != ""} { lappend attrList "$args/bInterfaceClass" + lappend mandatoryList "$args/bInterfaceClass" } foreach attr $attrList { if [file exists $dir/$attr] { set rc [open $dir/$attr r] - set usb($attr) [read -nonewline $rc] + set usb($attr) [string trim [read -nonewline $rc]] close $rc } else { set usb($attr) "" - Log "Warning: USB attribute \"$attr\" not found." + if {[lsearch $mandatoryList $attr] > -1} { + set result 0 + } + if {$attr == "serial"} {continue} + Log " Warning: USB attribute \"$attr\" not found" } } +return $result } # end of proc {ReadUSBAttrs} @@ -511,8 +534,7 @@ return 1 proc {ParseGlobalConfig} {} { -global logging noswitching - +global flags set configFile "" set places [list /etc/usb_modeswitch.conf /etc/sysconfig/usb_modeswitch /etc/default/usb_modeswitch] foreach cfg $places { @@ -521,20 +543,25 @@ foreach cfg $places { break } } - if {$configFile == ""} {return} set rc [open $configFile r] while {![eof $rc]} { gets $rc line + if [regexp {^#} [string trim $line]] {continue} if [regexp {DisableSwitching\s*=\s*([^\s]+)} $line d val] { if [regexp -nocase {1|yes|true} $val] { - set noswitching 1 + set flags(noswitching) 1 } } if [regexp {EnableLogging\s*=\s*([^\s]+)} $line d val] { if [regexp -nocase {1|yes|true} $val] { - set logging 1 + set flags(logging) 1 + } + } + if [regexp {SetStorageDelay\s*=\s*([^\s]+)} $line d val] { + if [regexp {\d+} $val] { + set flags(stordelay) $val } } @@ -545,25 +572,69 @@ return "Using global config file: $configFile" # end of proc {ParseGlobalConfig} -proc ParseDeviceConfig {configFile} { +proc ParseDeviceConfig {configContent} { global config set config(driverModule) "" set config(driverIDPath) "" set config(waitBefore) "" -set rc [open $configFile r] -set lineList [split [read $rc] \n] -close $rc -foreach line $lineList { - if [regexp {^DriverModule[[:blank:]]*=[[:blank:]]*"?(\w+)"?} [string trim $line] d config(driverModule)] { - Log "config: DriverModule set to $config(driverModule)" - } - if [regexp {^DriverIDPath[[:blank:]]*=[[:blank:]]*?"?([/\-\w]+)"?} [string trim $line] d config(driverIDPath)] { - Log "config: DriverIDPath set to $config(driverIDPath)" - } - if [regexp {^WaitBefore[[:blank:]]*=[[:blank:]]*?([0-9]+)} [string trim $line] d config(waitBefore)] { - Log "config: WaitBefore set to $config(waitBefore)" +set config(targetVendor) "" +set config(targetProduct) "" +set config(targetClass) "" +set config(Configuration) "" +set config(noMBIMCheck) 0 +set config(checkSuccess) 20 +set loadDriver 1 + +if [regexp -line {^[^#]*?TargetVendor.*?=.*?0x(\w+).*?$} $configContent d config(targetVendor)] { + Log "config: TargetVendor set to $config(targetVendor)" +} +if [regexp -line {^[^#]*?TargetProduct.*?=.*?0x(\w+).*?$} $configContent d config(targetProduct)] { + Log "config: TargetProduct set to $config(targetProduct)" +} +if [regexp -line {^[^#]*?TargetProductList.*?=.*?"([0-9a-fA-F,]+).*?$} $configContent d config(targetProduct)] { + Log "config: TargetProductList set to $config(targetProduct)" +} +if [regexp -line {^[^#]*?TargetClass.*?=.*?0x(\w+).*?$} $configContent d config(targetClass)] { + Log "config: TargetClass set to $config(targetClass)" +} +if [regexp -line {^[^#]*?Configuration.*?=.*?([0-9]+).*?$} $configContent d config(Configuration)] { + Log "config: Configuration (target) set to $config(Configuration)" +} +if [regexp -line {^[^#]*?DriverModule.*?=.*?(\w+).*?$} $configContent d config(driverModule)] { + Log "config: DriverModule set to $config(driverModule)" +} +if [regexp -line {^[^#]*?DriverIDPath.*?=.*?"?([/\-\w]+).*?$} $configContent d config(driverIDPath)] { + Log "config: DriverIDPath set to $config(driverIDPath)" +} +if [regexp -line {^[^#]*?CheckSuccess.*?=.*?([0-9]+).*?$} $configContent d config(checkSuccess)] { + Log "config: CheckSuccess set to $config(checkSuccess)" +} +if [regexp -line {^[^#]*?WaitBefore.*?=.*?([0-9]+).*?$} $configContent d config(waitBefore)] { + Log "config: WaitBefore set to $config(waitBefore)" +} +if [regexp -line {^[^#]*?NoMBIMCheck.*?=.*?([0-9]+).*?$} $configContent d config(noMBIMCheck)] { + Log "config: noMBIMCheck set to $config(noMBIMCheck)" +} +if [regexp -line {^[^#]*?NoDriverLoading.*?=.*?(1|yes|true).*?$} $configContent] { + set loadDriver 0 + Log "config: NoDriverLoading is set to active" +} + +# For general driver loading; TODO: add respective device names. +# Presently only useful for HSO devices (which are recounted now) +if $loadDriver { + if {$config(driverModule) == ""} { + set config(driverModule) "option" + set config(driverIDPath) "/sys/bus/usb-serial/drivers/option1" + } else { + if {$config(driverIDPath) == ""} { + set config(driverIDPath) "/sys/bus/usb/drivers/$config(driverModule)" + } } + Log "Driver module is \"$config(driverModule)\", ID path is $config(driverIDPath)\n" +} else { + Log "Driver will not be handled by usb_modeswitch" } set config(waitBefore) [string trimleft $config(waitBefore) 0] @@ -573,17 +644,17 @@ set config(waitBefore) [string trimleft $config(waitBefore) 0] proc ConfigGet {command config} { -global settings +global setup switch $command { conflist { # Unpackaged configs first; sorting is essential for priority - set configList [lsort -decreasing [glob -nocomplain $settings(dbdir_etc)/$config*]] - set configList [concat $configList [lsort -decreasing [glob -nocomplain $settings(dbdir)/$config*]]] - if [file exists $settings(dbdir)/configPack.tar.gz] { - Log "Found packed config collection $settings(dbdir)/configPack.tar.gz" - if [catch {set packedList [exec tar -tzf $settings(dbdir)/configPack.tar.gz 2>/dev/null]} err] { + set configList [lsort -decreasing [glob -nocomplain $setup(dbdir_etc)/$config*]] + set configList [concat $configList [lsort -decreasing [glob -nocomplain $setup(dbdir)/$config*]]] + if [file exists $setup(dbdir)/configPack.tar.gz] { + Log "Found packed config collection $setup(dbdir)/configPack.tar.gz" + if [catch {set packedList [exec tar -tzf $setup(dbdir)/configPack.tar.gz 2>/dev/null]} err] { Log "Error: problem opening config package; tar returned\n $err" return {} } @@ -600,22 +671,19 @@ switch $command { conffile { if [regexp {^pack/} $config] { set config [regsub {pack/} $config {}] - set settings(tmpConfig) /var/lib/usb_modeswitch/current_cfg - Log "Extracting config $config from collection $settings(dbdir)/configPack.tar.gz" - set wc [open $settings(tmpConfig) w] - puts -nonewline $wc [exec tar -xzOf $settings(dbdir)/configPack.tar.gz $config 2>/dev/null] - close $wc + Log "Extracting config $config from collection $setup(dbdir)/configPack.tar.gz" + set configContent [exec tar -xzOf $setup(dbdir)/configPack.tar.gz $config 2>/dev/null] } else { - if [regexp [list $settings(dbdir_etc)] $config] { - Log "Using config file from override folder $settings(dbdir_etc)" - set syslog_text "usb_modeswitch: using overriding config file $config; make sure this is intended" - catch {exec logger -p syslog.notice $syslog_text 2>/dev/null} - set syslog_text "usb_modeswitch: please report any new or corrected settings; otherwise, check for outdated files" - catch {exec logger -p syslog.notice $syslog_text 2>/dev/null} + if [regexp [list $setup(dbdir_etc)] $config] { + Log "Using config file from override folder $setup(dbdir_etc)" + SysLog "usb_modeswitch: using overriding config file $config; make sure this is intended" + SysLog "usb_modeswitch: please report any new or corrected settings; otherwise, check for outdated files" } - set settings(tmpConfig) $config + set rc [open $config r] + set configContent [read $rc] + close $rc } - return $settings(tmpConfig) + return $configContent } } @@ -624,17 +692,22 @@ switch $command { proc {Log} {msg} { -global wc logging device -if {$logging == 0} {return} -if {![info exists wc]} { - if [catch {set wc [open /var/log/usb_modeswitch_$device w]} err] { - set wc "error" - return +global flags device + +if {$flags(logging) == 0} {return} +if {![info exists flags(wc)]} { + if [catch {set flags(wc) [open /var/log/usb_modeswitch_$device w]} err] { + if [catch {set flags(wc) [open /dev/console w]} err] { + set flags(wc) "error" + return + } else { + puts $flags(wc) "Error opening log file ($err), redirect to console" + } } - puts $wc "\n\nUSB_ModeSwitch log from [clock format [clock seconds]]\n" + puts $flags(wc) "\n\nUSB_ModeSwitch log from [clock format [clock seconds]]\n" } -if {$wc == "error"} {return} -puts $wc $msg +if {$flags(wc) == "error"} {return} +puts $flags(wc) $msg } # end of proc {Log} @@ -643,9 +716,9 @@ puts $wc $msg # Closing the log file if open and exit proc {SafeExit} {} { -global wc -if [info exists wc] { - catch {close $wc} +global flags +if [info exists flags(wc)] { + catch {close $flags(wc)} } exit @@ -653,29 +726,23 @@ exit # end of proc {SafeExit} -# Checking for interrupt endpoint in ttyUSB port (lowest if there is -# more than one); if found, check for unused "gsmmodem[n]" name. -# Link for first modem will be "gsmmodem", then "gsmmodem2" and up -# proc {SymLinkName} {path} { global device -set loginit "* called with --symlink-name: params $path *\n" - -# Internal proc, used only here proc {hasInterrupt} {ifDir} { if {[llength [glob -nocomplain $ifDir/ttyUSB*]] == 0} { Log " no ttyUSB interface - skip checking endpoints" return 0 } foreach epDir [glob -nocomplain $ifDir/ep_*] { - Log " in epDir $epDir ..." + set e [file tail $epDir] + Log " checking $e ..." if [file exists $epDir/type] { set rc [open $epDir/type r] set type [read $rc] close $rc if [regexp {Interrupt} $type] { - Log " $epDir has interrupt transfer type" + Log " $e has interrupt transfer type" return 1 } } @@ -683,71 +750,56 @@ proc {hasInterrupt} {ifDir} { return 0 } +set loginit "usb_modeswitch called with --symlink-name\n parameter: $path\n" + # In case the device path is returned as /class/tty/ttyUSB, -# we need to extract the USB device path from symlink "device" +# get the USB device path from linked tree "device" set linkpath /sys$path/device if [file exists $linkpath] { if {[file type $linkpath] == "link"} { set rawpath [file readlink $linkpath] set trimpath [regsub -all {\.\./} $rawpath {}] if [file isdirectory /sys/$trimpath] { + append loginit "\n Using path $path\n" set path /$trimpath } } } - if {![regexp {ttyUSB[0-9]+} $path myPort]} { - Log "$loginit\nCould not find port name in path\n $path. Aborting" - return "" -} -set device $myPort -Log "$loginit\nMy name is $myPort" - -if {![regexp {usb[0-9]+/([0-9]+-[0-9]+)/} $path d dev_top]} { - Log "Could not find device directory in path\n $path. Aborting" + if $flags(logging) { + set device [clock clicks] + Log "$loginit\nThis is not a ttyUSB port. Aborting" + } return "" } -if {![regexp "\[0-9\]+\\.(\[0-9\]+)/$myPort" $path d myIf]} { - Log "Could not find interface number in path\n $path. Aborting" - return "" -} +set device $myPort +Log "$loginit\nMy name is $myPort\n" -if {![regexp "$dev_top:\[0-9\]" /sys$path ifRoot]} { - Log "Could not find interface number in path\n $path. Aborting" +if {![regexp {(.*?[0-9]+)\.([0-9]+)/ttyUSB} /sys$path d ifRoot ifNum]} { + Log "Could not find interface in path\n $path. Aborting" return "" } -set dirList [split $path /] -set idx [lsearch $dirList $dev_top] +set ifDir $ifRoot.$ifNum -set devDir /sys[join [lrange $dirList 0 $idx] /] - -Log "My port is $myPort, my interface is $myIf - devDir: $devDir\n dev_top: $dev_top\nsysPath: /sys$path" - -regexp "$devDir/$dev_top:\[0-9\]" /sys$path ifRoot - -set ifDir $ifRoot.$myIf - -Log "Checking my endpoints in $ifDir" +Log "Checking my endpoints ...\n in $ifDir" if [hasInterrupt $ifDir] { - Log "\n--> I am an interrupt port\n" + Log "\n--> I am an interrupt port" set rightPort 1 } else { Log "\n--> I am not an interrupt port\n" set rightPort 0 } -# Unfortunately, there are devices with more than one interrupt -# port. The assumption so far is that the lowest of these is -# right. Check all lower interfaces for annother one (if interface) -# is bigger than 0). If found, don't return any name. +# There are devices with more than one interrupt interface. +# Assume that the lowest of these is usable. Check all +# possible lower interfaces -if { $rightPort && ($myIf > 0) } { - Log "Looking for lower ports with interrupt endpoints" - for {set i 0} {$i < $myIf} {incr i} { +if { $rightPort && ($ifNum > 0) } { + Log "\nLooking for lower ports with interrupt endpoints" + for {set i 0} {$i < $ifNum} {incr i} { set ifDir $ifRoot.$i Log " in ifDir $ifDir ..." if [hasInterrupt $ifDir] { @@ -757,7 +809,6 @@ if { $rightPort && ($myIf > 0) } { } } } - if {$rightPort == 0} { Log "Return empty name and exit" return "" @@ -770,11 +821,15 @@ set idx 2 set symlinkName "gsmmodem" while {$idx < 256} { if {![file exists $symlinkName]} { + set placeholder [open /dev/$symlinkName w] + close $placeholder break } set symlinkName gsmmodem$idx incr idx } +if {$idx == 256} {return ""} + Log "Return symlink name \"$symlinkName\" and exit" return $symlinkName @@ -782,17 +837,17 @@ return $symlinkName # end of proc {SymLinkName} -# Load and bind (serial) driver +# Load and bind driver (default "option") # proc {CheckDriverBind} {vid pid} { global config -set loader "" -if [file exists /sbin/modprobe] { - set loader /sbin/modprobe -} else { - Log " /sbin/modprobe not found" +foreach fn {/sbin/modprobe /usr/sbin/modprobe} { + if [file exists $fn] { + set loader $fn + } } +Log "Module loader is $loader" set idfile $config(driverIDPath)/new_id if {![file exists $idfile]} { @@ -800,14 +855,14 @@ if {![file exists $idfile]} { Log "Can't do anymore without module loader; get \"modtools\"!" return } - Log "\nTrying to load driver \"$config(driverModule)\"" + Log "\nTrying to load module \"$config(driverModule)\"" if [catch {set result [exec $loader -v $config(driverModule)]} err] { Log " Running \"$loader $config(driverModule)\" gave an error:\n $err" } else { - Log " Driver was loaded successfully:\n$result" + Log " Module was loaded successfully:\n$result" } } else { - Log "Driver was loaded already" + Log "Module is active already" } set i 0 while {$i < 50} { @@ -819,26 +874,27 @@ while {$i < 50} { } if {$i < 50} { Log "Trying to add ID to driver \"$config(driverModule)\"" - catch {exec logger -p syslog.notice "usb_modeswitch: adding device ID $vid:$pid to driver \"$config(driverModule)\"" 2>/dev/null} - catch {exec logger -p syslog.notice "usb_modeswitch: please report the device ID to the Linux USB developers!" 2>/dev/null} - if [catch {exec echo "$vid $pid" >$idfile} err] { - Log "Error adding ID to driver: $err" + SysLog "usb_modeswitch: adding device ID $vid:$pid to driver \"$config(driverModule)\"" + SysLog "usb_modeswitch: please report the device ID to the Linux USB developers!" + if [catch {exec echo "$vid $pid ff" >$idfile} err] { + Log " Error adding ID to driver:\n $err" } else { Log " ID added to driver; check for new devices in /dev" } } else { - Log " \"$idfile\" not found, can't add ID to driver;\n check if kernel version is at least 2.6.27" + Log " \"$idfile\" not found, check if kernel version is at least 2.6.27" Log "Falling back to \"usbserial\"" set config(driverModule) usbserial - Log "\nTrying to unload driver \"$config(driverModule)\"" - if [catch {exec $loader -r $config(driverModule)} err] { - Log " Running \"$loader $config(driverModule)\" gave an error:\n $err" - Log "Can't unload usbserial. No more fallbacks" + Log "\nTrying to unload driver \"usbserial\"" + if [catch {exec $loader -r usbserial} err] { + Log " Running \"$loader -r usbserial\" gave an error:\n $err" + Log "No more fallbacks" return } + after 50 Log "\nTrying to load driver \"usbserial\" with device IDs" if [catch {set result [exec $loader -v usbserial vendor=0x$vid product=0x$pid]} err] { - Log " Running \"$loader $config(driverModule)\" gave an error:\n $err" + Log " Running \"$loader usbserial\" gave an error:\n $err" } else { Log " Driver was loaded successfully:\n$result" } @@ -891,9 +947,9 @@ if [file exists $listfile] { } lappend idList $id set buffer [join $idList "\n"] -if [catch {set wc [open $listfile w]}] {return} -puts $wc $buffer -close $wc +if [catch {set lc [open $listfile w]}] {return} +puts $lc $buffer +close $lc } # end of proc {AddToList} @@ -922,25 +978,158 @@ if {[llength $idList] == 0} { return } set buffer [join $idList "\n"] -if [catch {set wc [open $listfile w]}] {return} -puts $wc $buffer -close $wc +if [catch {set lc [open $listfile w]}] {return} +puts $lc $buffer +close $lc } # end of proc {RemoveFromBindList} -# Return a list with all relevant serial devices that are present -proc {ListSerialDevs} {} { -set devList [glob -nocomplain /dev/ttyUSB* /dev/ttyACM* /dev/ttyHS*] -if [file isdirectory /dev/tts] { - eval lappend devList [glob -nocomplain /dev/tts/*] +proc {CheckSuccess} {devdir} { + +global config usb +set ifdir [file tail [IfDir 0]] + +if {[string length $config(targetClass)] || [string length $config(Configuration)]} { + set config(targetVendor) $usb(idVendor) + set config(targetProduct) $usb(idProduct) +} +Log "Checking success of mode switch for max. $config(checkSuccess) seconds ..." + +for {set i 1} {$i <= $config(checkSuccess)} {incr i} { + after 1000 + if {![file isdirectory $devdir]} { + Log " Waiting for device file system ($i sec.) ..." + continue + } else { + Log " Reading attributes ..." + } + set ifdir [IfDir 0] + if {$ifdir == ""} {continue} + set ifdir [file tail $ifdir] + if {![ReadUSBAttrs $devdir $ifdir]} { + Log " Essential attributes are missing, continue wait ..." + continue + } + if [string length $config(targetClass)] { + if {![regexp $usb($ifdir/bInterfaceClass) $config(targetClass)]} {continue} + } + if [string length $config(Configuration)] { + if {$usb(bConfigurationValue) != $config(Configuration)} {continue} + } + if {![regexp $usb(idVendor) $config(targetVendor)]} {continue} + if {![regexp $usb(idProduct) $config(targetProduct)]} {continue} + Log " All attributes matched" + break +} +if {$i > 20} { + return 0 +} +return 1 + +} +# end of proc {CheckSuccess} + + +proc {ChkIface} {iface} { + +if {[IfClass $iface] == 8} { + return $iface +} else { + return -1 +} + +} +# end of proc {ChkIface} + +proc {IfDir} {iface} { + +global devdir +set allfiles [glob -nocomplain $devdir/*] +set files [glob -nocomplain $devdir/*.$iface] +if {[llength $files] == 0} { + return "" +} +set ifdir [lindex $files 0] +if {![file isdirectory $ifdir]} { + return "" +} +return $ifdir + +} +# end of proc {IfDir} + +proc {IfClass} {iface} { + +set ifdir [IfDir $iface] + +if {![file exists $ifdir/bInterfaceClass]} { + return -1 } -return $devList +set rc [open $ifdir/bInterfaceClass r] +set c [read $rc] +close $rc +return [string trimleft [string trim $c] 0] + +} +# end of proc {IfClass} + + +proc {SysLog} {msg} { + +global flags +if {![info exists flags(logger)]} { + set flags(logger) "" + foreach fn {/bin/logger /usr/bin/logger} { + if [file exists $fn] { + set flags(logger) $fn + } + } + Log "Logger is $flags(logger)" +} +if {$flags(logger) == ""} { + Log "Can't add system message, no syslog helper found" + return +} +catch {exec $flags(logger) -p syslog.notice "$msg" 2>/dev/null} + +} +# end of proc {SysLog} + +proc {SetStorageDelay} {secs} { + +Log "Adjusting delay for USB storage devices ..." +set attrib /sys/module/usb_storage/parameters/delay_use +if {![file exists $attrib]} { + Log "Error: could not find delay_use attribute" + return +} +if [catch {set ch [open $attrib r+]} err] { + Log "Error: could not access delay_use attribute: $err" + return +} +if {[read $ch] < $secs} { + seek $ch 0 start + puts -nonewline $ch $secs + Log " Delay set to $secs seconds\n" +} else { + Log " Current value is higher than $secs. Leave it alone\n" +} +close $ch + +} +# end of proc {SetStorageDelay} + +proc {CheckMBIM} {} { + +set kversion [exec uname -r] +if [file exists /lib/modules/$kversion/kernel/drivers/net/usb/cdc_mbim.ko] {return 1} +if [file exists /sys/bus/usb/drivers/cdc_mbim] {return 1} +return 0 } -# end of proc {ListSerialDevs} # The actual entry point -Main $argc $argv +Main $argv $argc diff --git a/release/src/router/usbmodeswitch/usb_modeswitch_dispatcher b/release/src/router/usbmodeswitch/usb_modeswitch_dispatcher new file mode 100644 index 0000000000..7f8c37d2d6 --- /dev/null +++ b/release/src/router/usbmodeswitch/usb_modeswitch_dispatcher @@ -0,0 +1,1135 @@ +#!/usr/bin/tclsh + +# Wrapper (tcl) for usb_modeswitch, called from +# /lib/udev/rules.d/40-usb_modeswitch.rules +# (part of data pack "usb-modeswitch-data") via +# /lib/udev/usb_modeswitch +# +# Does ID check on newly discovered USB devices and calls +# the mode switching program with the matching parameter +# file from /usr/share/usb_modeswitch +# +# Part of usb-modeswitch-1.2.6 package +# (C) Josua Dietze 2009-2013 + +set arg0 [lindex $argv 0] +if [regexp {\.tcl$} $arg0] { + if [file exists $arg0] { + set argv [lrange $argv 1 end] + source $arg0 + exit + } +} + +# Setting of these switches is done in the global config +# file (/etc/usb_modeswitch.conf) if available + +set flags(logging) 0 +set flags(noswitching) 0 +set flags(stordelay) 0 + +# Execution starts at file bottom + +proc {Main} {argv argc} { + +global scsi usb config match device flags setup devdir + +set loginit [ParseGlobalConfig] + +# The facility to add a symbolic link pointing to the +# ttyUSB port which provides interrupt transfer, i.e. +# the port to connect through. +# Will check for interrupt endpoint in ttyUSB port (lowest if +# there is more than one); if found, return "gsmmodem[n]" name +# to udev for symlink creation + +# This is run once for every port of LISTED devices by +# an udev rule + +if {[lindex $argv 0] == "--symlink-name"} { + puts -nonewline [SymLinkName [lindex $argv 1]] + SafeExit +} + +set argList [split [lindex $argv 1] /] +if [string length [lindex $argList 1]] { + set device [lindex $argList 1] +} else { + set device "noname" +} + +if {$flags(stordelay) > 0} { + SetStorageDelay $flags(stordelay) +} + +Log "Raw args from udev: [lindex $argv 1]\n\n$loginit" +if {$device == "noname"} { + Log "No data from udev. Exiting" + SafeExit +} + +if {[lindex $argv 0] != "--switch-mode"} { + Log "No command given. Exiting" + SafeExit +} + +set setup(dbdir) /usr/share/usb_modeswitch +set setup(dbdir_etc) /etc/usb_modeswitch.d + + +if {![file exists $setup(dbdir)] && ![file exists $setup(dbdir_etc)]} { + Log "Error: no config database found in /usr/share or /etc. Exiting" + SafeExit +} +set bindir /usr/sbin + +set devList1 {} +set devList2 {} + + +# arg 0: the bus id for the device (udev: %b) +# arg 1: the "kernel name" for the device (udev: %k) +# +# Used to determine the top directory for the device in sysfs + +set ifChk 0 +if {[string length [lindex $argList 0]] == 0} { + if {[string length [lindex $argList 1]] == 0} { + Log "No device number values given from udev! Exiting" + SafeExit + } else { + if {![regexp {(.*?):} [lindex $argList 1] d dev_top]} { + if [regexp {([0-9]+-[0-9]+\.?[0-9]*.*)} [lindex $argList 1] d dev_top] { + # new udev rules file, got to check class of first interface + Log "Called by new rules file - remember to check class of first interface ..." + set ifChk 1 + } else { + Log "Could not determine device dir from udev values! Exiting" + SafeExit + } + } + } +} else { + set dev_top [lindex $argList 0] + regexp {(.*?):} $dev_top d dev_top +} + +# NC +#set dev_top [lindex $argList 1] +# + +set devdir /sys/bus/usb/devices/$dev_top +if {![file isdirectory $devdir]} { + Log "Top device directory not found ($devdir)! Exiting" + SafeExit +} +Log "Using top device dir $devdir" + + +# Mapping of the short string identifiers (in the config +# file names) to the long name used here +# +# If we need them it's a snap to add new attributes here! + +set match(sVe) scsi(vendor) +set match(sMo) scsi(model) +set match(sRe) scsi(rev) +set match(uMa) usb(manufacturer) +set match(uPr) usb(product) +set match(uSe) usb(serial) + + +# Now reading the USB attributes +if {![ReadUSBAttrs $devdir]} { + Log "USB attributes not found in sysfs tree. Exiting" + SafeExit +} + +set iface 0 +if $ifChk { + Log "Check class of first interface ..." + set iface [ChkIface 0] + if {$iface < 0} { + set iface [ChkIface 9] + } + if {$iface < 0} { + if {$usb(idVendor)=="19d2" && $usb(idProduct)=="2000"} { + set iface [ChkIface 3] + } +# Corrected, was wrongly reported +# if {$usb(idVendor)=="16d8" && $usb(idProduct)=="6803"} { +# set iface [ChkIface 3] +# } + } + if {$iface < 0} { + Log " Device is not in install mode. Exiting" + SafeExit + } else { + Log " Device is in install mode." + } +} +set ifdir [file tail [IfDir $iface]] +regexp {:([0-9]+\.[0-9]+)$} $ifdir d iface + +Log "Using interface $iface" + + +if $flags(logging) { + Log "----------------\nUSB values from sysfs:" + foreach attr {manufacturer product serial} { + Log " $attr\t$usb($attr)" + } + Log "----------------" +} + +if $flags(noswitching) { + Log "\nSwitching globally disabled. Exiting\n" + SysLog "usb_modeswitch: switching disabled, no action for $usb(idVendor):$usb(idProduct)" + SafeExit +} + +if {$usb(bNumConfigurations) == "1"} { + set configParam "-u -1" + Log "bNumConfigurations is 1 - don't check for active configuration" +} else { + set configParam "" +} + +# Check if there is more than one config file for this USB ID, +# which would make an attribute test necessary. If so, check if +# SCSI values are needed + +set configList [ConfigGet conflist $usb(idVendor):$usb(idProduct)] + +if {[llength $configList] == 0} { + Log "Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exiting" + SafeExit +} + +set scsiNeeded 0 +if {[llength $configList] > 1} { + if [regexp {:s} $configList] { + set scsiNeeded 1 + } +} +if $scsiNeeded { + if [ReadSCSIAttrs $devdir:$iface] { + Log "----------------\nSCSI values from sysfs:" + foreach attr {vendor model rev} { + Log " $attr\t$scsi($attr)" + } + Log "----------------" + } else { + Log "Could not get SCSI attributes, exclude devices with SCSI match" + } +} else { + Log "SCSI attributes not needed, moving on" +} + +# General wait - this is important +after 500 + +# Now check for a matching config file. Matching is done +# by MatchDevice + +set report {} +foreach configuration $configList { + + # skipping installer leftovers + if [regexp {\.(dpkg|rpm)} $configuration] {continue} + + Log "checking config: $configuration" + if [MatchDevice $configuration] { + Log "! matched. Reading config data" + if [string length $usb(busnum)] { + set busParam "-b [string trimleft $usb(busnum) 0]" + set devParam "-g [string trimleft $usb(devnum) 0]" + } else { + set busParam "" + set devParam "" + } + set configBuffer [ConfigGet conffile $configuration] + ParseDeviceConfig $configBuffer + if {$config(waitBefore) == ""} { + } else { + Log " waiting time set to $config(waitBefore) seconds" + append config(waitBefore) "000" + after $config(waitBefore) + Log " waiting is over, switching starts now" + } + if {$config(noMBIMCheck)==0 && $usb(bNumConfigurations) > 1} { + Log "Device may have an MBIM configuration, checking driver ..." + if [CheckMBIM] { + Log " driver for MBIM devices is available" + Log "Finding MBIM configuration number ..." + if [catch {set cfgno [exec /usr/sbin/usb_modeswitch -j -Q $busParam $devParam -v $usb(idVendor) -p $usb(idProduct)]} err] { + Log "Error when trying to find MBIM configuration, switch to legacy modem mode" + } else { + set cfgno [string trim $cfgno] + if {$cfgno > 0} { + set config(Configuration) $cfgno + set config(driverModule) "" + set configBuffer "Configuration=$cfgno" + } else { + Log " No MBIM configuration found, switch to legacy modem mode" + } + } + } else { + Log " no MBIM driver found, switch to legacy modem mode" + } + } + + # Now we are actually switching + if $flags(logging) { + Log "Command to be run:\nusb_modeswitch -W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f \$configBuffer" + set report [exec /usr/sbin/usb_modeswitch -W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$configBuffer" 2>@ stdout] + Log "\nVerbose debug output of usb_modeswitch and libusb follows" + Log "(Note that some USB errors are to be expected in the process)" + Log "--------------------------------" + Log $report + Log "--------------------------------" + Log "(end of usb_modeswitch output)\n" + } else { + set report [exec /usr/sbin/usb_modeswitch -Q -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$configBuffer" 2>@ stdout] + } + break + } else { + Log "* no match, not switching with this config" + } +} + +# Switching is complete; success checking was either +# done by usb_modeswitch and logged via syslog OR bus/dev +# parameter were used; then we do check for success HERE + +if [regexp {ok:busdev} $report] { + if [CheckSuccess $devdir] { + Log "Mode switching was successful, found $usb(idVendor):$usb(idProduct) ($usb(manufacturer): $usb(product))" + SysLog "usb_modeswitch: switched to $usb(idVendor):$usb(idProduct) on [format %03d $usb(busnum)]/[format %03d $usb(devnum)]" + } else { + Log "\nTarget config not matching - current values are" + set attrList {idVendor idProduct bConfigurationValue manufacturer product serial} + foreach attr [lsort [array names usb]] { + Log " [format %-26s $attr:] $usb($attr)" + } + Log "\nMode switching may have failed. Exiting\n" + SafeExit + } +} else { + if {![file isdirectory $devdir]} { + Log "Device directory in sysfs is gone! Something went wrong, aborting" + SafeExit + } + if {![regexp {ok:} $report]} { + Log "\nCore program reported switching failure. Exiting\n" + SafeExit + } + # Give the device another second if it's not fully back yet + if {![file exists $devdir/idProduct]} { + after 1000 + } + ReadUSBAttrs $devdir $ifdir +} + +# Now checking for bound drivers (only for class 0xff) + +if {$config(driverModule) != "" && $usb($ifdir/bInterfaceClass) != "" && [regexp {ok:} $report]} { + if {$usb($ifdir/bInterfaceClass) != "ff"} { + set config(driverModule) "" + Log " No vendor-specific class found, skip driver checking" + } +} + +# If module is set (it is by default), driver shall be loaded. +# If not, then NoDriverLoading is active + +if {$config(driverModule) != ""} { + if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} { + if {![regexp {ok:(\w{4}):(\w{4})} $report d usb(idVendor) usb(idProduct)]} { + Log "No target vendor/product ID found or given, can't continue. Aborting" + SafeExit + } + } + # wait for any drivers to bind automatically + after 1000 + Log "Now checking for bound driver ..." + if {![file exists $devdir/$ifdir/driver]} { + Log " no driver has bound to interface 0 yet" + AddToList link_list $usb(idVendor):$usb(idProduct) + + # If device is known, the sh wrapper will take care, else: + if {[InBindList $usb(idVendor):$usb(idProduct)] == 0} { + Log "Device is not in \"bind_list\" yet, bind it now" + + # Load driver + CheckDriverBind $usb(idVendor) $usb(idProduct) + + # Old/slow systems may take a while to create the devices + set counter 0 + while {![file exists $devdir/$ifdir/driver]} { + if {$counter == 14} {break} + after 500 + incr counter + } + if {$counter == 14} { + Log " driver binding failed" + } else { + Log " driver was bound to the device" + AddToList bind_list $usb(idVendor):$usb(idProduct) + } + } + } else { + Log " driver has bound, device is known" + if {[llength [glob -nocomplain $devdir/$ifdir/ttyUSB*]] > 0} { + AddToList link_list $usb(idVendor):$usb(idProduct) + } + } +} else { + # Just in case "NoDriverLoading" was added after the first bind + RemoveFromBindList $usb(idVendor):$usb(idProduct) +} + +if [regexp {ok:$} $report] { + # "NoDriverLoading" was set + Log "Doing no driver checking or binding for this device" +} + +# In newer kernels there is a switch to avoid the use of a device +# reset (e.g. from usb-storage) which would possibly switch back +# a mode-switching device to initial mode +if [regexp {ok:} $report] { + Log "Checking for AVOID_RESET_QUIRK kernel attribute" + if [file exists $devdir/avoid_reset_quirk] { + if [catch {exec echo "1" >$devdir/avoid_reset_quirk 2>/dev/null} err] { + Log " Error setting the attribute: $err" + } else { + Log " AVOID_RESET_QUIRK activated" + } + } else { + Log " not present in this kernel" + } +} + +Log "\nAll done, exiting\n" +SafeExit + +} +# end of proc {Main} + + +proc {ReadSCSIAttrs} {topdir} { + +global scsi +set counter 0 +set sysdir $topdir +Log "Checking storage tree in sysfs ..." +while {$counter < 20} { + Log " loop $counter/20" + if {![file isdirectory $sysdir]} { + # Device is gone. Unplugged? Switched by kernel? + Log " sysfs device tree is gone; abort SCSI value check" + return 0 + } + # Searching the storage/SCSI tree; might take a while + if {[set dirList [glob -nocomplain $topdir/host*]] != ""} { + set sysdir [lindex $dirList 0] + if {[set dirList [glob -nocomplain $sysdir/target*]] != ""} { + set sysdir [lindex $dirList 0] + regexp {.*target(.*)} $sysdir d subdir + if {[set dirList [glob -nocomplain $sysdir/$subdir*]] != ""} { + set sysdir [lindex $dirList 0] + if [file exists $sysdir/vendor] { + Log " Storage tree is ready" + break + } + } + } + } + after 500 + incr counter +} +if {$counter == 20} { + Log "SCSI tree not found; you may want to check if this path/file exists:" + Log "$sysdir/vendor\n" + return 0 +} + +Log "Reading SCSI values ..." +foreach attr {vendor model rev} { + if [file exists $sysdir/$attr] { + set rc [open $sysdir/$attr r] + set scsi($attr) [read -nonewline $rc] + close $rc + } else { + set scsi($attr) "" + Log "Warning: SCSI attribute \"$attr\" not found." + } +} +return 1 + +} +# end of proc {ReadSCSIAttrs} + + +proc {ReadUSBAttrs} {dir args} { + +global usb + +set attrList {idVendor idProduct bConfigurationValue manufacturer product serial devnum busnum bNumConfigurations} +set mandatoryList {idVendor idProduct bNumConfigurations} +set result 1 +if {$args != ""} { + lappend attrList "$args/bInterfaceClass" + lappend mandatoryList "$args/bInterfaceClass" +} +foreach attr $attrList { + if [file exists $dir/$attr] { + set rc [open $dir/$attr r] + set usb($attr) [string trim [read -nonewline $rc]] + close $rc + } else { + set usb($attr) "" + if {[lsearch $mandatoryList $attr] > -1} { + set result 0 + } + if {$attr == "serial"} {continue} + Log " Warning: USB attribute \"$attr\" not found" + } +} +return $result + +} +# end of proc {ReadUSBAttrs} + + +proc {MatchDevice} {config} { + +global scsi usb match + +set devinfo [file tail $config] +set infoList [split $devinfo :] +set stringList [lrange $infoList 2 end] +if {[llength $stringList] == 0} {return 1} + +foreach teststring $stringList { + if {$teststring == "?"} {return 0} + set tokenList [split $teststring =] + set id [lindex $tokenList 0] + set matchstring [lindex $tokenList 1] + set blankstring "" + regsub -all {_} $matchstring { } blankstring + Log "matching $match($id)" + Log " match string1 (exact): $matchstring" + Log " match string2 (blanks): $blankstring" + Log " device string: [set $match($id)]" + if {!([string match *$matchstring* [set $match($id)]] || [string match *$blankstring* [set $match($id)]])} { + return 0 + } +} +return 1 + +} +# end of proc {MatchDevice} + + +proc {ParseGlobalConfig} {} { + +global flags +set configFile "" +set places [list /etc/usb_modeswitch.conf /etc/sysconfig/usb_modeswitch /etc/default/usb_modeswitch] +foreach cfg $places { + if [file exists $cfg] { + set configFile $cfg + break + } +} +if {$configFile == ""} {return} + +set rc [open $configFile r] +while {![eof $rc]} { + gets $rc line + if [regexp {^#} [string trim $line]] {continue} + if [regexp {DisableSwitching\s*=\s*([^\s]+)} $line d val] { + if [regexp -nocase {1|yes|true} $val] { + set flags(noswitching) 1 + } + } + if [regexp {EnableLogging\s*=\s*([^\s]+)} $line d val] { + if [regexp -nocase {1|yes|true} $val] { + set flags(logging) 1 + } + } + if [regexp {SetStorageDelay\s*=\s*([^\s]+)} $line d val] { + if [regexp {\d+} $val] { + set flags(stordelay) $val + } + } + +} +return "Using global config file: $configFile" + +} +# end of proc {ParseGlobalConfig} + + +proc ParseDeviceConfig {configContent} { + +global config +set config(driverModule) "" +set config(driverIDPath) "" +set config(waitBefore) "" +set config(targetVendor) "" +set config(targetProduct) "" +set config(targetClass) "" +set config(Configuration) "" +set config(noMBIMCheck) 0 +set config(checkSuccess) 20 +set loadDriver 1 + +if [regexp -line {^[^#]*?TargetVendor.*?=.*?0x(\w+).*?$} $configContent d config(targetVendor)] { + Log "config: TargetVendor set to $config(targetVendor)" +} +if [regexp -line {^[^#]*?TargetProduct.*?=.*?0x(\w+).*?$} $configContent d config(targetProduct)] { + Log "config: TargetProduct set to $config(targetProduct)" +} +if [regexp -line {^[^#]*?TargetProductList.*?=.*?"([0-9a-fA-F,]+).*?$} $configContent d config(targetProduct)] { + Log "config: TargetProductList set to $config(targetProduct)" +} +if [regexp -line {^[^#]*?TargetClass.*?=.*?0x(\w+).*?$} $configContent d config(targetClass)] { + Log "config: TargetClass set to $config(targetClass)" +} +if [regexp -line {^[^#]*?Configuration.*?=.*?([0-9]+).*?$} $configContent d config(Configuration)] { + Log "config: Configuration (target) set to $config(Configuration)" +} +if [regexp -line {^[^#]*?DriverModule.*?=.*?(\w+).*?$} $configContent d config(driverModule)] { + Log "config: DriverModule set to $config(driverModule)" +} +if [regexp -line {^[^#]*?DriverIDPath.*?=.*?"?([/\-\w]+).*?$} $configContent d config(driverIDPath)] { + Log "config: DriverIDPath set to $config(driverIDPath)" +} +if [regexp -line {^[^#]*?CheckSuccess.*?=.*?([0-9]+).*?$} $configContent d config(checkSuccess)] { + Log "config: CheckSuccess set to $config(checkSuccess)" +} +if [regexp -line {^[^#]*?WaitBefore.*?=.*?([0-9]+).*?$} $configContent d config(waitBefore)] { + Log "config: WaitBefore set to $config(waitBefore)" +} +if [regexp -line {^[^#]*?NoMBIMCheck.*?=.*?([0-9]+).*?$} $configContent d config(noMBIMCheck)] { + Log "config: noMBIMCheck set to $config(noMBIMCheck)" +} +if [regexp -line {^[^#]*?NoDriverLoading.*?=.*?(1|yes|true).*?$} $configContent] { + set loadDriver 0 + Log "config: NoDriverLoading is set to active" +} + +# For general driver loading; TODO: add respective device names. +# Presently only useful for HSO devices (which are recounted now) +if $loadDriver { + if {$config(driverModule) == ""} { + set config(driverModule) "option" + set config(driverIDPath) "/sys/bus/usb-serial/drivers/option1" + } else { + if {$config(driverIDPath) == ""} { + set config(driverIDPath) "/sys/bus/usb/drivers/$config(driverModule)" + } + } + Log "Driver module is \"$config(driverModule)\", ID path is $config(driverIDPath)\n" +} else { + Log "Driver will not be handled by usb_modeswitch" +} +set config(waitBefore) [string trimleft $config(waitBefore) 0] + +} +# end of proc {ParseDeviceConfig} + + +proc ConfigGet {command config} { + +global setup + +switch $command { + + conflist { + # Unpackaged configs first; sorting is essential for priority + set configList [lsort -decreasing [glob -nocomplain $setup(dbdir_etc)/$config*]] + set configList [concat $configList [lsort -decreasing [glob -nocomplain $setup(dbdir)/$config*]]] + if [file exists $setup(dbdir)/configPack.tar.gz] { + Log "Found packed config collection $setup(dbdir)/configPack.tar.gz" + if [catch {set packedList [exec tar -tzf $setup(dbdir)/configPack.tar.gz 2>/dev/null]} err] { + Log "Error: problem opening config package; tar returned\n $err" + return {} + } + set packedList [split $packedList \n] + set packedConfigList [lsort -decreasing [lsearch -glob -all -inline $packedList $config*]] + # Now add packaged configs with a mark, again sorted for priority + foreach packedConfig $packedConfigList { + lappend configList "pack/$packedConfig" + } + } + + return $configList + } + conffile { + if [regexp {^pack/} $config] { + set config [regsub {pack/} $config {}] + Log "Extracting config $config from collection $setup(dbdir)/configPack.tar.gz" + set configContent [exec tar -xzOf $setup(dbdir)/configPack.tar.gz $config 2>/dev/null] + } else { + if [regexp [list $setup(dbdir_etc)] $config] { + Log "Using config file from override folder $setup(dbdir_etc)" + SysLog "usb_modeswitch: using overriding config file $config; make sure this is intended" + SysLog "usb_modeswitch: please report any new or corrected settings; otherwise, check for outdated files" + } + set rc [open $config r] + set configContent [read $rc] + close $rc + } + return $configContent + } +} + +} +# end of proc {ConfigGet} + +proc {Log} {msg} { + +global flags device + +if {$flags(logging) == 0} {return} +if {![info exists flags(wc)]} { + if [catch {set flags(wc) [open /var/log/usb_modeswitch_$device w]} err] { + if [catch {set flags(wc) [open /dev/console w]} err] { + set flags(wc) "error" + return + } else { + puts $flags(wc) "Error opening log file ($err), redirect to console" + } + } + puts $flags(wc) "\n\nUSB_ModeSwitch log from [clock format [clock seconds]]\n" +} +if {$flags(wc) == "error"} {return} +puts $flags(wc) $msg + +} +# end of proc {Log} + + +# Closing the log file if open and exit +proc {SafeExit} {} { + +global flags +if [info exists flags(wc)] { + catch {close $flags(wc)} +} +exit + +} +# end of proc {SafeExit} + + +proc {SymLinkName} {path} { +global device + +proc {hasInterrupt} {ifDir} { + if {[llength [glob -nocomplain $ifDir/ttyUSB*]] == 0} { + Log " no ttyUSB interface - skip checking endpoints" + return 0 + } + foreach epDir [glob -nocomplain $ifDir/ep_*] { + set e [file tail $epDir] + Log " checking $e ..." + if [file exists $epDir/type] { + set rc [open $epDir/type r] + set type [read $rc] + close $rc + if [regexp {Interrupt} $type] { + Log " $e has interrupt transfer type" + return 1 + } + } + } + return 0 +} + +set loginit "usb_modeswitch called with --symlink-name\n parameter: $path\n" + +# In case the device path is returned as /class/tty/ttyUSB, +# get the USB device path from linked tree "device" +set linkpath /sys$path/device +if [file exists $linkpath] { + if {[file type $linkpath] == "link"} { + set rawpath [file readlink $linkpath] + set trimpath [regsub -all {\.\./} $rawpath {}] + if [file isdirectory /sys/$trimpath] { + append loginit "\n Using path $path\n" + set path /$trimpath + } + } +} + +if {![regexp {ttyUSB[0-9]+} $path myPort]} { + if $flags(logging) { + set device [clock clicks] + Log "$loginit\nThis is not a ttyUSB port. Aborting" + } + return "" +} + +set device $myPort +Log "$loginit\nMy name is $myPort\n" + +if {![regexp {(.*?[0-9]+)\.([0-9]+)/ttyUSB} /sys$path d ifRoot ifNum]} { + Log "Could not find interface in path\n $path. Aborting" + return "" +} + +set ifDir $ifRoot.$ifNum + +Log "Checking my endpoints ...\n in $ifDir" +if [hasInterrupt $ifDir] { + Log "\n--> I am an interrupt port" + set rightPort 1 +} else { + Log "\n--> I am not an interrupt port\n" + set rightPort 0 +} + +# There are devices with more than one interrupt interface. +# Assume that the lowest of these is usable. Check all +# possible lower interfaces + +if { $rightPort && ($ifNum > 0) } { + Log "\nLooking for lower ports with interrupt endpoints" + for {set i 0} {$i < $ifNum} {incr i} { + set ifDir $ifRoot.$i + Log " in ifDir $ifDir ..." + if [hasInterrupt $ifDir] { + Log "\n--> found an interrupt interface below me\n" + set rightPort 0 + break + } + } +} +if {$rightPort == 0} { + Log "Return empty name and exit" + return "" +} + +Log "\n--> No interrupt interface below me\n" + +cd /dev +set idx 2 +set symlinkName "gsmmodem" +while {$idx < 256} { + if {![file exists $symlinkName]} { + set placeholder [open /dev/$symlinkName w] + close $placeholder + break + } + set symlinkName gsmmodem$idx + incr idx +} +if {$idx == 256} {return ""} + +Log "Return symlink name \"$symlinkName\" and exit" +return $symlinkName + +} +# end of proc {SymLinkName} + + +# Load and bind driver (default "option") +# +proc {CheckDriverBind} {vid pid} { +global config + +foreach fn {/sbin/modprobe /usr/sbin/modprobe} { + if [file exists $fn] { + set loader $fn + } +} +Log "Module loader is $loader" + +set idfile $config(driverIDPath)/new_id +if {![file exists $idfile]} { + if {$loader == ""} { + Log "Can't do anymore without module loader; get \"modtools\"!" + return + } + Log "\nTrying to load module \"$config(driverModule)\"" + if [catch {set result [exec $loader -v $config(driverModule)]} err] { + Log " Running \"$loader $config(driverModule)\" gave an error:\n $err" + } else { + Log " Module was loaded successfully:\n$result" + } +} else { + Log "Module is active already" +} +set i 0 +while {$i < 50} { + if [file exists $idfile] { + break + } + after 20 + incr i +} +if {$i < 50} { + Log "Trying to add ID to driver \"$config(driverModule)\"" + SysLog "usb_modeswitch: adding device ID $vid:$pid to driver \"$config(driverModule)\"" + SysLog "usb_modeswitch: please report the device ID to the Linux USB developers!" + if [catch {exec echo "$vid $pid ff" >$idfile} err] { + Log " Error adding ID to driver:\n $err" + } else { + Log " ID added to driver; check for new devices in /dev" + } +} else { + Log " \"$idfile\" not found, check if kernel version is at least 2.6.27" + Log "Falling back to \"usbserial\"" + set config(driverModule) usbserial + Log "\nTrying to unload driver \"usbserial\"" + if [catch {exec $loader -r usbserial} err] { + Log " Running \"$loader -r usbserial\" gave an error:\n $err" + Log "No more fallbacks" + return + } + after 50 + Log "\nTrying to load driver \"usbserial\" with device IDs" + if [catch {set result [exec $loader -v usbserial vendor=0x$vid product=0x$pid]} err] { + Log " Running \"$loader usbserial\" gave an error:\n $err" + } else { + Log " Driver was loaded successfully:\n$result" + } +} + +} +# end of proc {CheckDriverBind} + + +# Check if USB ID is listed as needing driver binding +proc {InBindList} {id} { + +set listfile /var/lib/usb_modeswitch/bind_list +if {![file exists $listfile]} {return 0} +set rc [open $listfile r] +set buffer [read $rc] +close $rc +if [string match *$id* $buffer] { +Log "Found $id in bind_list" + return 1 +} else { +Log "No $id in bind_list" + return 0 +} + +} +# end of proc {InBindList} + +# Add USB ID to list of devices needing later treatment +proc {AddToList} {name id} { + +set listfile /var/lib/usb_modeswitch/$name +set oldlistfile /etc/usb_modeswitch.d/bind_list + +if {($name == "bind_list") && [file exists $oldlistfile] && ![file exists $listfile]} { + if [catch {file rename $oldlistfile $listfile} err] { + Log "Error renaming the old bind list file ($err)" + return + } +} + +if [file exists $listfile] { + set rc [open $listfile r] + set buffer [read $rc] + close $rc + if [string match *$id* $buffer] { + return + } + set idList [split [string trim $buffer] \n] +} +lappend idList $id +set buffer [join $idList "\n"] +if [catch {set lc [open $listfile w]}] {return} +puts $lc $buffer +close $lc + +} +# end of proc {AddToList} + + +# Remove USB ID from bind list (NoDriverLoading is set) +proc {RemoveFromBindList} {id} { + +set listfile /var/lib/usb_modeswitch/bind_list +if [file exists $listfile] { + set rc [open $listfile r] + set buffer [read $rc] + close $rc + set idList [split [string trim $buffer] \n] +} else { + return +} +set idx [lsearch $idList $id] +if {$idx > -1} { + set idList [lreplace $idList $idx $idx] +} else { + return +} +if {[llength $idList] == 0} { + file delete $listfile + return +} +set buffer [join $idList "\n"] +if [catch {set lc [open $listfile w]}] {return} +puts $lc $buffer +close $lc + +} +# end of proc {RemoveFromBindList} + + +proc {CheckSuccess} {devdir} { + +global config usb +set ifdir [file tail [IfDir 0]] + +if {[string length $config(targetClass)] || [string length $config(Configuration)]} { + set config(targetVendor) $usb(idVendor) + set config(targetProduct) $usb(idProduct) +} +Log "Checking success of mode switch for max. $config(checkSuccess) seconds ..." + +for {set i 1} {$i <= $config(checkSuccess)} {incr i} { + after 1000 + if {![file isdirectory $devdir]} { + Log " Waiting for device file system ($i sec.) ..." + continue + } else { + Log " Reading attributes ..." + } + set ifdir [IfDir 0] + if {$ifdir == ""} {continue} + set ifdir [file tail $ifdir] + if {![ReadUSBAttrs $devdir $ifdir]} { + Log " Essential attributes are missing, continue wait ..." + continue + } + if [string length $config(targetClass)] { + if {![regexp $usb($ifdir/bInterfaceClass) $config(targetClass)]} {continue} + } + if [string length $config(Configuration)] { + if {$usb(bConfigurationValue) != $config(Configuration)} {continue} + } + if {![regexp $usb(idVendor) $config(targetVendor)]} {continue} + if {![regexp $usb(idProduct) $config(targetProduct)]} {continue} + Log " All attributes matched" + break +} +if {$i > 20} { + return 0 +} +return 1 + +} +# end of proc {CheckSuccess} + + +proc {ChkIface} {iface} { + +if {[IfClass $iface] == 8} { + return $iface +} else { + return -1 +} + +} +# end of proc {ChkIface} + +proc {IfDir} {iface} { + +global devdir +set allfiles [glob -nocomplain $devdir/*] +set files [glob -nocomplain $devdir/*.$iface] +if {[llength $files] == 0} { + return "" +} +set ifdir [lindex $files 0] +if {![file isdirectory $ifdir]} { + return "" +} +return $ifdir + +} +# end of proc {IfDir} + +proc {IfClass} {iface} { + +set ifdir [IfDir $iface] + +if {![file exists $ifdir/bInterfaceClass]} { + return -1 +} +set rc [open $ifdir/bInterfaceClass r] +set c [read $rc] +close $rc +return [string trimleft [string trim $c] 0] + +} +# end of proc {IfClass} + + +proc {SysLog} {msg} { + +global flags +if {![info exists flags(logger)]} { + set flags(logger) "" + foreach fn {/bin/logger /usr/bin/logger} { + if [file exists $fn] { + set flags(logger) $fn + } + } + Log "Logger is $flags(logger)" +} +if {$flags(logger) == ""} { + Log "Can't add system message, no syslog helper found" + return +} +catch {exec $flags(logger) -p syslog.notice "$msg" 2>/dev/null} + +} +# end of proc {SysLog} + +proc {SetStorageDelay} {secs} { + +Log "Adjusting delay for USB storage devices ..." +set attrib /sys/module/usb_storage/parameters/delay_use +if {![file exists $attrib]} { + Log "Error: could not find delay_use attribute" + return +} +if [catch {set ch [open $attrib r+]} err] { + Log "Error: could not access delay_use attribute: $err" + return +} +if {[read $ch] < $secs} { + seek $ch 0 start + puts -nonewline $ch $secs + Log " Delay set to $secs seconds\n" +} else { + Log " Current value is higher than $secs. Leave it alone\n" +} +close $ch + +} +# end of proc {SetStorageDelay} + +proc {CheckMBIM} {} { + +set kversion [exec uname -r] +if [file exists /lib/modules/$kversion/kernel/drivers/net/usb/cdc_mbim.ko] {return 1} +if [file exists /sys/bus/usb/drivers/cdc_mbim] {return 1} +return 0 + +} + + +# The actual entry point +Main $argv $argc -- 2.11.4.GIT