mirror of
https://github.com/tio/tio.git
synced 2026-05-01 14:57:59 +02:00
Compare commits
988 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fb3a64ba2 | ||
|
|
3af4c5591e | ||
|
|
cce94b9d92 | ||
|
|
86f48a2fb6 | ||
|
|
381c0b7823 | ||
|
|
8f33cff6ea | ||
|
|
9d00cd3492 | ||
|
|
3e0b2d861d | ||
|
|
a1217af4c6 | ||
|
|
58bf5c5008 | ||
|
|
7e61a34df3 | ||
|
|
2fb788f817 | ||
|
|
f887756a71 | ||
|
|
5d915134a3 | ||
|
|
7516dff802 | ||
|
|
03ef931fb2 | ||
|
|
437881f0ed | ||
|
|
c736b1e353 | ||
|
|
d682e98a66 | ||
|
|
bdfe87e1cb | ||
|
|
5c2ced1093 | ||
|
|
f87f470415 | ||
|
|
2e86718973 | ||
|
|
013aebcc05 | ||
|
|
b33045189f | ||
|
|
600c3d7563 | ||
|
|
ebce2d4ee9 | ||
|
|
16b7aee42f | ||
|
|
d33e275ca3 | ||
|
|
da4074c9a5 | ||
|
|
f716d2ccdd | ||
|
|
7567e08227 | ||
|
|
6aca9ffee5 | ||
|
|
f5740dbf31 | ||
|
|
d163afc6b1 | ||
|
|
1b60dd1ae7 | ||
|
|
795ef28f79 | ||
|
|
8e155c9276 | ||
|
|
6831ad0eae | ||
|
|
8f7bf2fd2c | ||
|
|
f389f11669 | ||
|
|
37994b3cc5 | ||
|
|
27f8f2c4e6 | ||
|
|
01e637cdf4 | ||
|
|
1b2a0ea130 | ||
|
|
b8135ea639 | ||
|
|
c49faa7337 | ||
|
|
4511d74a9e | ||
|
|
afd82f7ac4 | ||
|
|
db3f109c7d | ||
|
|
ab678e6c88 | ||
|
|
330e99381e | ||
|
|
7e314b2cc3 | ||
|
|
4fb034858a | ||
|
|
d494b9d3ac | ||
|
|
4034d0ad51 | ||
|
|
9fec689117 | ||
|
|
6c4b92270e | ||
|
|
a22b270749 | ||
|
|
9f27ce5899 | ||
|
|
27f9b9f2e8 | ||
|
|
2e7da862c8 | ||
|
|
bb2b4e30b2 | ||
|
|
f47467271f | ||
|
|
cdc773100c | ||
|
|
a3b67d3eb6 | ||
|
|
ef12ed62df | ||
|
|
2f6b3796f2 | ||
|
|
6163bc392b | ||
|
|
475bc29cc8 | ||
|
|
c4f5269c83 | ||
|
|
13ad59ac12 | ||
|
|
2259244eb2 | ||
|
|
725423c50c | ||
|
|
14963032c3 | ||
|
|
e1fe232254 | ||
|
|
9cafcbcab5 | ||
|
|
f4076258f1 | ||
|
|
866b5bcb30 | ||
|
|
289bbfd393 | ||
|
|
68a64ac554 | ||
|
|
0a95da00f1 | ||
|
|
32d97683f8 | ||
|
|
c3654486c7 | ||
|
|
b5184012c4 | ||
|
|
fc0c6f61d2 | ||
|
|
53bb2a6ad1 | ||
|
|
da9f7a6540 | ||
|
|
2a1dae6336 | ||
|
|
ada2db0739 | ||
|
|
14ee38a0d9 | ||
|
|
2c700a90b0 | ||
|
|
da04c2c444 | ||
|
|
5f70b75e90 | ||
|
|
02cac07a77 | ||
|
|
a3a6b5127f | ||
|
|
ef6fa8030e | ||
|
|
561376696b | ||
|
|
d34fa1c1ad | ||
|
|
9022f51ea5 | ||
|
|
4723cc3f4e | ||
|
|
8c471105fe | ||
|
|
b9902bbd78 | ||
|
|
c5afd86b9a | ||
|
|
b756d2e1f1 | ||
|
|
5c7d81b900 | ||
|
|
7e9574f98d | ||
|
|
053ae53f19 | ||
|
|
8f77ad5830 | ||
|
|
be4fc0908f | ||
|
|
6ec2ac19d0 | ||
|
|
bed34a0b9a | ||
|
|
134038c1ce | ||
|
|
003b2e37d4 | ||
|
|
8014ef68c0 | ||
|
|
563c4fa6ea | ||
|
|
d1d6b45e8e | ||
|
|
f148a1413c | ||
|
|
bb3636e2d5 | ||
|
|
133789517a | ||
|
|
7e0bd980f2 | ||
|
|
883acbaa4b | ||
|
|
d10e762a7d | ||
|
|
94e40d82f3 | ||
|
|
9315cf6a55 | ||
|
|
eb9726bbcc | ||
|
|
4014fc4b3e | ||
|
|
ccc01433b7 | ||
|
|
ee3687430b | ||
|
|
6f6038ebcd | ||
|
|
0921796054 | ||
|
|
dba4690a88 | ||
|
|
b3aac7b182 | ||
|
|
5b5248929e | ||
|
|
8f45d6f688 | ||
|
|
37f8b4fd1b | ||
|
|
5f5a8a9cdd | ||
|
|
3f616a47c8 | ||
|
|
694524cb6f | ||
|
|
747ac62733 | ||
|
|
c76a4d0172 | ||
|
|
d2dd9f5a5b | ||
|
|
ae9c8edbca | ||
|
|
f71ffeabb7 | ||
|
|
6ebd50ab85 | ||
|
|
241ff93bf4 | ||
|
|
2d17624ddb | ||
|
|
ce70f43113 | ||
|
|
f825363606 | ||
|
|
86f1b3881d | ||
|
|
c2f910ffe3 | ||
|
|
9066523229 | ||
|
|
016c81291e | ||
|
|
e75e19eb00 | ||
|
|
545d473220 | ||
|
|
873bd6973d | ||
|
|
9320f54a73 | ||
|
|
68e2042fd6 | ||
|
|
b490233988 | ||
|
|
c2ef2fced5 | ||
|
|
3a75b098d1 | ||
|
|
242a2ea843 | ||
|
|
ce736c267a | ||
|
|
c88cd3c5f3 | ||
|
|
59940b3311 | ||
|
|
05785e82b3 | ||
|
|
50253a6a77 | ||
|
|
13f3bedb2f | ||
|
|
6310a9fabc | ||
|
|
eb087713a4 | ||
|
|
3e81f36dce | ||
|
|
fe4e47219e | ||
|
|
ec8f63f06d | ||
|
|
5ec33f5d4d | ||
|
|
adafa00b87 | ||
|
|
31647a934c | ||
|
|
e9c96c5456 | ||
|
|
3b3fca2e8b | ||
|
|
a53a4f44de | ||
|
|
60caede5dd | ||
|
|
eae7f8f8d7 | ||
|
|
04dfa682c9 | ||
|
|
62a4a93dec | ||
|
|
7aa2d3fee2 | ||
|
|
22bcfdc29f | ||
|
|
0e9dbcbc77 | ||
|
|
68c78222e1 | ||
|
|
22b4f451ea | ||
|
|
15ba034ce5 | ||
|
|
17bb6edfd2 | ||
|
|
65c5a068d8 | ||
|
|
68d3b845b2 | ||
|
|
22f030ebb8 | ||
|
|
f5703ff107 | ||
|
|
3b77eb35cf | ||
|
|
054326454b | ||
|
|
b763f1289b | ||
|
|
8ead9337d1 | ||
|
|
d8fb141bc4 | ||
|
|
a698799a7d | ||
|
|
5e3722a10e | ||
|
|
c16a2a1f94 | ||
|
|
e2960c3f82 | ||
|
|
08404e700f | ||
|
|
6d77201ba0 | ||
|
|
eef0a15194 | ||
|
|
6895c05321 | ||
|
|
c453728ab5 | ||
|
|
273afb73f4 | ||
|
|
84dd4c3685 | ||
|
|
33eae0c30d | ||
|
|
9bc93991e7 | ||
|
|
ed70a587ec | ||
|
|
fe2973522e | ||
|
|
f5f62ee02d | ||
|
|
cd160250a6 | ||
|
|
588ac3e8ac | ||
|
|
76de9b890e | ||
|
|
42ff234204 | ||
|
|
4113a072c2 | ||
|
|
232cbee697 | ||
|
|
1b77ed783b | ||
|
|
41b8e4f99c | ||
|
|
c61d56935b | ||
|
|
6e779a0520 | ||
|
|
d8fbd607d4 | ||
|
|
01f3c391f0 | ||
|
|
5bbdf3b9f8 | ||
|
|
b4741de50c | ||
|
|
d19ba1c492 | ||
|
|
ae76f8f58d | ||
|
|
b05f38abd0 | ||
|
|
51bfa68bdd | ||
|
|
fa4207ddfd | ||
|
|
3ca6a66d9b | ||
|
|
f1c5394570 | ||
|
|
0105346a11 | ||
|
|
96fafc5fb4 | ||
|
|
29546bb13a | ||
|
|
b0e9fa02e8 | ||
|
|
f257b7fba5 | ||
|
|
6fff4939e4 | ||
|
|
a8e0d2693d | ||
|
|
c440da2ea8 | ||
|
|
f1144ca5cc | ||
|
|
3cc2d90fda | ||
|
|
48c9e8a9a9 | ||
|
|
f4c4387e05 | ||
|
|
a1987b61b4 | ||
|
|
f8924182d3 | ||
|
|
d0e95c5fba | ||
|
|
1cefb7b6bc | ||
|
|
794c5202f4 | ||
|
|
98653566a8 | ||
|
|
a605533213 | ||
|
|
1e20948d83 | ||
|
|
4801816357 | ||
|
|
d60363a64c | ||
|
|
5c45150f58 | ||
|
|
d3993da6d4 | ||
|
|
00f3ea9b7f | ||
|
|
e8114ca0a4 | ||
|
|
de0a7c547f | ||
|
|
76a7a56e85 | ||
|
|
9744fcafcf | ||
|
|
c4878a90d7 | ||
|
|
ae461dc296 | ||
|
|
10eedd4ad2 | ||
|
|
fa41771e65 | ||
|
|
a1a4dc4642 | ||
|
|
7dbb806311 | ||
|
|
97537853a8 | ||
|
|
e1e3e298bf | ||
|
|
c5dac4fd33 | ||
|
|
51300cc4f0 | ||
|
|
3ad090caf7 | ||
|
|
fba73f98db | ||
|
|
2db87ede53 | ||
|
|
fca76a017d | ||
|
|
7915c1a445 | ||
|
|
6c75ec553d | ||
|
|
fc54df1f22 | ||
|
|
0afae5d3ee | ||
|
|
e028544cd0 | ||
|
|
5eb649278a | ||
|
|
418a43d96e | ||
|
|
00c8124a0a | ||
|
|
78f96bd32c | ||
|
|
d8fb1ab0ca | ||
|
|
a208c9908a | ||
|
|
2b6a79b9f0 | ||
|
|
2fff4d36d0 | ||
|
|
fd6a246908 | ||
|
|
901e00bba3 | ||
|
|
eea46a2005 | ||
|
|
2ee1f5c224 | ||
|
|
3e50191107 | ||
|
|
4369d5b66f | ||
|
|
70913fe120 | ||
|
|
83f826349b | ||
|
|
00f57c9992 | ||
|
|
0becfa3274 | ||
|
|
6fee8514f4 | ||
|
|
10255d52d0 | ||
|
|
6720da3b88 | ||
|
|
ed4ac0c797 | ||
|
|
d45c9b1a22 | ||
|
|
91459c2490 | ||
|
|
593f9495f4 | ||
|
|
6c520090c6 | ||
|
|
67640d4a00 | ||
|
|
58c9489b92 | ||
|
|
553b67e406 | ||
|
|
4269ec835d | ||
|
|
e572255fd2 | ||
|
|
bfefd04b55 | ||
|
|
ed66c72ca1 | ||
|
|
14f598e11e | ||
|
|
7193e6f0c9 | ||
|
|
72399c4fe6 | ||
|
|
1777206de7 | ||
|
|
1c32555c2a | ||
|
|
4307e81760 | ||
|
|
3d7d9c85b5 | ||
|
|
02b60e9fb3 | ||
|
|
c93922fd36 | ||
|
|
838c110876 | ||
|
|
a42f3f78d1 | ||
|
|
93e49ab5a2 | ||
|
|
ed0386d2c4 | ||
|
|
63dced047f | ||
|
|
cf6e8b963b | ||
|
|
07864a0e78 | ||
|
|
c9c5f03c10 | ||
|
|
e6ffbd9058 | ||
|
|
812dee8e54 | ||
|
|
d9dc1ff698 | ||
|
|
c01baca157 | ||
|
|
5c441f22c2 | ||
|
|
8134cd3486 | ||
|
|
46a72d8254 | ||
|
|
bdca5a27ec | ||
|
|
3c7c865e59 | ||
|
|
59cd3a1379 | ||
|
|
98052936b0 | ||
|
|
a486ba581b | ||
|
|
df5379bac5 | ||
|
|
5656381cc3 | ||
|
|
d461751a71 | ||
|
|
88ef473362 | ||
|
|
d3bd5d8e17 | ||
|
|
148a3c1da1 | ||
|
|
3bedd85e7c | ||
|
|
ab905d8e0c | ||
|
|
8fe5dde4b8 | ||
|
|
b5ca54c56e | ||
|
|
fd1003533c | ||
|
|
ec2cf476bb | ||
|
|
afc9e3be5b | ||
|
|
5651c1c5d7 | ||
|
|
65b3353f57 | ||
|
|
791b1df27e | ||
|
|
f5c8d5233c | ||
|
|
5635f30135 | ||
|
|
cbb8ad8362 | ||
|
|
b540f16734 | ||
|
|
2f3583ebde | ||
|
|
36e4c672a7 | ||
|
|
cee560dd8b | ||
|
|
fcf760f0d7 | ||
|
|
a6f1b8d546 | ||
|
|
4f509594d8 | ||
|
|
4008969ca7 | ||
|
|
c16e004757 | ||
|
|
309a2abfd4 | ||
|
|
269974c244 | ||
|
|
0cdc3ce045 | ||
|
|
f6905b4213 | ||
|
|
725faa5455 | ||
|
|
e1774664dd | ||
|
|
35c9b40982 | ||
|
|
b533312d45 | ||
|
|
c6f319ecb0 | ||
|
|
b6672c1704 | ||
|
|
ae604d12e2 | ||
|
|
4782faf47f | ||
|
|
4a1c213409 | ||
|
|
27b8c1b82a | ||
|
|
76eb17e4d4 | ||
|
|
a2bbab0684 | ||
|
|
dfe5f827f9 | ||
|
|
ea7f43e60c | ||
|
|
4e9d29c88d | ||
|
|
6a955b08e1 | ||
|
|
20a40edd36 | ||
|
|
42739c0817 | ||
|
|
2915d26a19 | ||
|
|
e2e974958e | ||
|
|
6b084a53b8 | ||
|
|
a21809b090 | ||
|
|
cd24797961 | ||
|
|
f4dc46d002 | ||
|
|
93e6efc001 | ||
|
|
12f20c84e3 | ||
|
|
419fbdc3fa | ||
|
|
a4f0d4da53 | ||
|
|
1aaecef419 | ||
|
|
6c19cd0750 | ||
|
|
845e683fc9 | ||
|
|
d3ac8e7b1f | ||
|
|
c6beed8265 | ||
|
|
7c32e5a21e | ||
|
|
73c1129be7 | ||
|
|
374c2ad113 | ||
|
|
c80f833a06 | ||
|
|
784201ea2e | ||
|
|
2cba4b863f | ||
|
|
fba56318f8 | ||
|
|
48a2298e81 | ||
|
|
6f9e41152b | ||
|
|
134fa87a5c | ||
|
|
eaab692d4d | ||
|
|
a40097f837 | ||
|
|
b00b0e872f | ||
|
|
4768db5311 | ||
|
|
c2aa3489fd | ||
|
|
ad9b93dc52 | ||
|
|
a97b2c00cd | ||
|
|
bf7b032649 | ||
|
|
14fc77ffc1 | ||
|
|
ca5af47e7b | ||
|
|
0850b1938b | ||
|
|
c8aeba7ef8 | ||
|
|
a65b5b0e3a | ||
|
|
5f14924c96 | ||
|
|
0b61aaf451 | ||
|
|
acefbbbe6c | ||
|
|
4924969268 | ||
|
|
5b8f33bd78 | ||
|
|
ce3101a380 | ||
|
|
e54a13827b | ||
|
|
d70af20454 | ||
|
|
f2535e4ea0 | ||
|
|
fb89b79b30 | ||
|
|
3298ba0f1b | ||
|
|
d06623dd7a | ||
|
|
6c168bcc77 | ||
|
|
512bc410f3 | ||
|
|
52c89ca071 | ||
|
|
33f287e8cf | ||
|
|
167988bf27 | ||
|
|
3e728a223c | ||
|
|
1f19ba1302 | ||
|
|
3fab077a7a | ||
|
|
0131a9a051 | ||
|
|
e7dcd5d034 | ||
|
|
7d597d315b | ||
|
|
0f31f17f5d | ||
|
|
97796395aa | ||
|
|
f43ef9b584 | ||
|
|
529692805c | ||
|
|
76a51a4af3 | ||
|
|
eb95a656b0 | ||
|
|
bf3974c2b6 | ||
|
|
ab885713b9 | ||
|
|
81607424f8 | ||
|
|
d0926b25ba | ||
|
|
6618642acf | ||
|
|
47827f094b | ||
|
|
137e6111f6 | ||
|
|
61b204aa3d | ||
|
|
16479c8d86 | ||
|
|
278750428c | ||
|
|
f8c9d6091a | ||
|
|
75c5261fe5 | ||
|
|
8188bb5b76 | ||
|
|
0afcef807b | ||
|
|
1dc3a3891d | ||
|
|
ba2d49d2f7 | ||
|
|
a85cfcf3ef | ||
|
|
6224fe292e | ||
|
|
88588100e4 | ||
|
|
9cad01791b | ||
|
|
989874ffb8 | ||
|
|
dbc9a8e82d | ||
|
|
68fc159654 | ||
|
|
3c8d7fa786 | ||
|
|
8afbc1d813 | ||
|
|
ee46686fb6 | ||
|
|
a58d406a3c | ||
|
|
fe6827bc7e | ||
|
|
be7f77e214 | ||
|
|
0dd703e5a2 | ||
|
|
d2aafc24cc | ||
|
|
c12709f9fe | ||
|
|
db765bc371 | ||
|
|
1219c6b8c0 | ||
|
|
34e9d028a1 | ||
|
|
e837fd0303 | ||
|
|
a75e04b883 | ||
|
|
614517bfc4 | ||
|
|
f82d2353d4 | ||
|
|
b3a0917bc9 | ||
|
|
72eb682267 | ||
|
|
75aa066bf8 | ||
|
|
15bef05179 | ||
|
|
a9b7284ba2 | ||
|
|
0407436624 | ||
|
|
99c7aa85ed | ||
|
|
4598345168 | ||
|
|
4952c6ca13 | ||
|
|
70f69899fc | ||
|
|
1fb0cad7b9 | ||
|
|
dd785f9a30 | ||
|
|
31459f851b | ||
|
|
4c9f28203d | ||
|
|
463415d63b | ||
|
|
b5273b2b23 | ||
|
|
0eb59b09cf | ||
|
|
e8efb621f8 | ||
|
|
a133a241d6 | ||
|
|
c3116b2b56 | ||
|
|
a13fe254f2 | ||
|
|
d28007f0d3 | ||
|
|
c24cbfcd34 | ||
|
|
f454bdaa3a | ||
|
|
b322ecbdcf | ||
|
|
de606d51ba | ||
|
|
0cdd69940f | ||
|
|
443e07ef8a | ||
|
|
dd8b137b15 | ||
|
|
2fb1963da2 | ||
|
|
b082b0593b | ||
|
|
b1102b59e0 | ||
|
|
616003a55f | ||
|
|
6462aac792 | ||
|
|
5f46136b28 | ||
|
|
eadcc7e384 | ||
|
|
a0a8dccd51 | ||
|
|
40c8753151 | ||
|
|
4031636bce | ||
|
|
02637a92ca | ||
|
|
3bb0ffdeff | ||
|
|
c57f2c9fba | ||
|
|
e96d289718 | ||
|
|
cbff112ec3 | ||
|
|
a01d8b38b1 | ||
|
|
62c00ce05e | ||
|
|
3903880106 | ||
|
|
35f249394c | ||
|
|
34e95bb4a5 | ||
|
|
0bbaf4a296 | ||
|
|
5fd3bf60ba | ||
|
|
f26abe91b8 | ||
|
|
110aa1d9dd | ||
|
|
fc3e6c40ec | ||
|
|
4c2d4ed67f | ||
|
|
371a34d23a | ||
|
|
8b77111bb1 | ||
|
|
c931d7690d | ||
|
|
862a3ecadd | ||
|
|
7b5529b31f | ||
|
|
019e93a0ed | ||
|
|
9733bdf19c | ||
|
|
9db7bb4fbb | ||
|
|
52a1e65bc3 | ||
|
|
b7ff637667 | ||
|
|
d510c101b4 | ||
|
|
02729c10b1 | ||
|
|
1c53af0681 | ||
|
|
670c31d6f7 | ||
|
|
eac3dac018 | ||
|
|
00ae3b2fbd | ||
|
|
98db32b251 | ||
|
|
80977e2f1d | ||
|
|
96a035a56d | ||
|
|
223f0c5d13 | ||
|
|
e1c7a5c69a | ||
|
|
44d1812556 | ||
|
|
b2ab7a69a9 | ||
|
|
611406c14f | ||
|
|
dc50b23bec | ||
|
|
3792e10484 | ||
|
|
ff1ed7ea3f | ||
|
|
252545f9e6 | ||
|
|
dfcda015c3 | ||
|
|
a23be7f2c2 | ||
|
|
ac859f41b9 | ||
|
|
73a30a89ef | ||
|
|
c009e19d89 | ||
|
|
0af6c00764 | ||
|
|
360deb3c72 | ||
|
|
bf972bd2f9 | ||
|
|
39af74f263 | ||
|
|
8b8dd373a7 | ||
|
|
dad6de7ee7 | ||
|
|
11762b2300 | ||
|
|
555e526ce7 | ||
|
|
5b82c710f1 | ||
|
|
52446f4d62 | ||
|
|
8ccd52e9f3 | ||
|
|
8532388b7e | ||
|
|
f459a462c5 | ||
|
|
232c94eb79 | ||
|
|
1c29aac9fa | ||
|
|
944ee9173b | ||
|
|
dbdb84743f | ||
|
|
cf0d27e5b6 | ||
|
|
290ffb7a41 | ||
|
|
9c498140de | ||
|
|
d08758f247 | ||
|
|
6503963e4a | ||
|
|
f24cee61e7 | ||
|
|
a717631207 | ||
|
|
4e7456a9fc | ||
|
|
55f82c9a20 | ||
|
|
e5d71a5e51 | ||
|
|
e53b69afbd | ||
|
|
167a19fcdd | ||
|
|
36e091842e | ||
|
|
736c1ed003 | ||
|
|
028bd34cbe | ||
|
|
dc1b3a3549 | ||
|
|
5ed5f0ae7d | ||
|
|
72fd2f2980 | ||
|
|
8b6a0cf63c | ||
|
|
f83e81e5db | ||
|
|
6a1d4838da | ||
|
|
683aab1dc7 | ||
|
|
d598fa76a7 | ||
|
|
a56db8744b | ||
|
|
62d9b5fb5c | ||
|
|
4cb7eaf48d | ||
|
|
d1733fc090 | ||
|
|
732c0c3f89 | ||
|
|
eecfa6485c | ||
|
|
835effd3e9 | ||
|
|
3243dfa55c | ||
|
|
458ebc251d | ||
|
|
bd9ea4a295 | ||
|
|
eb24e7215b | ||
|
|
e426323d93 | ||
|
|
44e99ffcb0 | ||
|
|
16d6807de7 | ||
|
|
c10c47343e | ||
|
|
daa8cd39f3 | ||
|
|
5d62c2947b | ||
|
|
58568ed630 | ||
|
|
a3f5f6414b | ||
|
|
233152f0db | ||
|
|
1a9b983dcd | ||
|
|
e61e2d3820 | ||
|
|
b72fae4c87 | ||
|
|
5bf7ae6382 | ||
|
|
2bc8cf4206 | ||
|
|
5daabf88d9 | ||
|
|
4ca7375cac | ||
|
|
db88a9d664 | ||
|
|
1b91fd9f69 | ||
|
|
3553fc765e | ||
|
|
538e81cbe5 | ||
|
|
4b912981c3 | ||
|
|
0b4dfbe4d3 | ||
|
|
98c96dba52 | ||
|
|
d522527c49 | ||
|
|
4e08c68533 | ||
|
|
941e8d5b04 | ||
|
|
a947985651 | ||
|
|
ac25e7434c | ||
|
|
a0f3f9d9b7 | ||
|
|
4c611e6767 | ||
|
|
c82b7e2ecc | ||
|
|
3d65e62e52 | ||
|
|
f62e2f972b | ||
|
|
e72a2ede76 | ||
|
|
d7a95ed383 | ||
|
|
b46dcc46ac | ||
|
|
1bd663086a | ||
|
|
1f94fe2c9a | ||
|
|
09240c9ad7 | ||
|
|
574d5620b2 | ||
|
|
1f4a50cc9f | ||
|
|
edcd3c3c3d | ||
|
|
18de31a4c3 | ||
|
|
1dabf0fdf5 | ||
|
|
f48402f7ed | ||
|
|
1b2f445ed9 | ||
|
|
a826bf4fcb | ||
|
|
858b898bbb | ||
|
|
4b928a54b2 | ||
|
|
cd1e6529db | ||
|
|
dba77eb912 | ||
|
|
a37ad26a88 | ||
|
|
1636a55b7e | ||
|
|
1e8c950e9d | ||
|
|
cd03af190c | ||
|
|
256d6b4fc2 | ||
|
|
9333876ac7 | ||
|
|
b8b0ec6575 | ||
|
|
1debdc98cb | ||
|
|
1b99a103cb | ||
|
|
9ab846a30a | ||
|
|
5708e2187f | ||
|
|
db45326d98 | ||
|
|
01a58dd319 | ||
|
|
63d3232b32 | ||
|
|
318bcbf650 | ||
|
|
18b8f8140d | ||
|
|
c66017f287 | ||
|
|
92fbe86c49 | ||
|
|
fcfeb9189c | ||
|
|
8f7050dd86 | ||
|
|
f335ff63a9 | ||
|
|
b5c3193411 | ||
|
|
16c42bb9ae | ||
|
|
94485ec9ac | ||
|
|
9b55f18c07 | ||
|
|
0a892006ea | ||
|
|
bf749aead4 | ||
|
|
a0d4be068b | ||
|
|
bd5f542959 | ||
|
|
c38ba262d3 | ||
|
|
81a3dbd68a | ||
|
|
1117dc603e | ||
|
|
17e96d70bc | ||
|
|
ba22191800 | ||
|
|
373dca3d5b | ||
|
|
50478e7df3 | ||
|
|
46b5783789 | ||
|
|
2519e2081a | ||
|
|
625f32d288 | ||
|
|
1f094b6b68 | ||
|
|
e7282731f3 | ||
|
|
e854ca0efa | ||
|
|
77ff1e5269 | ||
|
|
14a16908a1 | ||
|
|
0a22142957 | ||
|
|
10199f6053 | ||
|
|
54830e3528 | ||
|
|
2109a0063b | ||
|
|
387f47a39c | ||
|
|
989efeae88 | ||
|
|
d41649afb6 | ||
|
|
6d007d39d7 | ||
|
|
0b55981e52 | ||
|
|
7d3b687eb4 | ||
|
|
a5bde2ea51 | ||
|
|
732a768970 | ||
|
|
d907834f72 | ||
|
|
5b7191ed7c | ||
|
|
8a83d2a973 | ||
|
|
9476422922 | ||
|
|
8d2ad8d82d | ||
|
|
d43ef3333f | ||
|
|
6b91159fdf | ||
|
|
be2c682da6 | ||
|
|
b0590fb021 | ||
|
|
ba201c7248 | ||
|
|
4e89858816 | ||
|
|
b0c085debd | ||
|
|
89574b2469 | ||
|
|
fb1fc9b7a1 | ||
|
|
6e0fcd1280 | ||
|
|
836c28098c | ||
|
|
5b6a106b34 | ||
|
|
c94e8826fd | ||
|
|
c96c3c4c83 | ||
|
|
c7b27b022f | ||
|
|
9bc6e34e82 | ||
|
|
9a1afc09c2 | ||
|
|
8da06ebf2c | ||
|
|
16cba00c54 | ||
|
|
2369c0558d | ||
|
|
334ae28c76 | ||
|
|
fa91a3fcc4 | ||
|
|
2e0b0386dc | ||
|
|
a81c43a01a | ||
|
|
b882827b97 | ||
|
|
b0b0856a8b | ||
|
|
7096bc3a96 | ||
|
|
45c32f78a8 | ||
|
|
af93efb9e4 | ||
|
|
97a4c415bf | ||
|
|
09bb6a34bb | ||
|
|
fb453160ef | ||
|
|
03e41b61a3 | ||
|
|
78b8c134ea | ||
|
|
28c880cdab | ||
|
|
11df828f68 | ||
|
|
c95226cb46 | ||
|
|
ac77f2dbb9 | ||
|
|
a543fbd7ef | ||
|
|
1ec39d1c7b | ||
|
|
f229ee2f96 | ||
|
|
8975c445c2 | ||
|
|
c4d14c1d76 | ||
|
|
c3969ec81a | ||
|
|
6c5e748e15 | ||
|
|
09bcd91bf0 | ||
|
|
03cf4ef197 | ||
|
|
ed97a8dc21 | ||
|
|
e81e76473a | ||
|
|
410fc8bffe | ||
|
|
659336af51 | ||
|
|
8965b7b42c | ||
|
|
a2b164519f | ||
|
|
ea0dd3e602 | ||
|
|
745244df06 | ||
|
|
e9d5a23129 | ||
|
|
d7b038d4ef | ||
|
|
02c0c61e07 | ||
|
|
25123fefea | ||
|
|
2e5c1ff20c | ||
|
|
2795ef6c79 | ||
|
|
04da651f09 | ||
|
|
0a825d87cf | ||
|
|
b23397caa0 | ||
|
|
166d36d7d4 | ||
|
|
3773fddb6a | ||
|
|
950373d10d | ||
|
|
39a9c4bbda | ||
|
|
e11fcbd2f4 | ||
|
|
88974852ae | ||
|
|
3c12616128 | ||
|
|
690fbebfc7 | ||
|
|
9f2cac4e41 | ||
|
|
8f63f755da | ||
|
|
45210bc741 | ||
|
|
d8a822a3fb | ||
|
|
47694904cf | ||
|
|
63ef65804f | ||
|
|
7ffeeccdf5 | ||
|
|
27f4d31380 | ||
|
|
cbfa118a2b | ||
|
|
a1afc2bcfa | ||
|
|
2d1e0f1233 | ||
|
|
3d08787142 | ||
|
|
1ae889fdb2 | ||
|
|
0e62995e6e | ||
|
|
a699561211 | ||
|
|
274cb63197 | ||
|
|
32e853e46e | ||
|
|
75a7e06d5d | ||
|
|
2f51e8368f | ||
|
|
6a57695ab3 | ||
|
|
883e3b0bea | ||
|
|
32113076d6 | ||
|
|
3599b84ad2 | ||
|
|
d4c3751405 | ||
|
|
f788ae2ca4 | ||
|
|
cdfdbee9ee | ||
|
|
1d03313848 | ||
|
|
b36b97769a | ||
|
|
137b2823af | ||
|
|
2375bc0c7b | ||
|
|
ff4b37af53 | ||
|
|
5d031a51f9 | ||
|
|
0985f70c13 | ||
|
|
2ea4b973f0 | ||
|
|
b6eac3f001 | ||
|
|
5efd519d2e | ||
|
|
16836de0e5 | ||
|
|
7e74f8cbe0 | ||
|
|
bb300293cc | ||
|
|
353ce69130 | ||
|
|
c0c9fcc00c | ||
|
|
59c9b867c3 | ||
|
|
64ed36966b | ||
|
|
bfea34a978 | ||
|
|
4e2c6725d5 | ||
|
|
e9208d693e | ||
|
|
c01152bb38 | ||
|
|
7a2a18232d | ||
|
|
6310f28d2c | ||
|
|
d35c607d1a | ||
|
|
c2024368a0 | ||
|
|
c6d996550a | ||
|
|
4931c30317 | ||
|
|
65153c0d03 | ||
|
|
101e32749b | ||
|
|
b46ea8b758 | ||
|
|
febefa638f | ||
|
|
95389f4dde | ||
|
|
aa9f121a12 | ||
|
|
3a1fd79fcb | ||
|
|
c0c21b1814 | ||
|
|
0f0279bd3f | ||
|
|
1079991608 | ||
|
|
17fafd9918 | ||
|
|
76c04f5015 | ||
|
|
457075629d | ||
|
|
a4da50dbd8 | ||
|
|
07639da625 | ||
|
|
cda42b0dbb | ||
|
|
161f613e08 | ||
|
|
960c3c0f6c | ||
|
|
553939e002 | ||
|
|
50e33a1a5b | ||
|
|
354bd6e185 | ||
|
|
3a1eea216a | ||
|
|
6c86faf6f9 | ||
|
|
3195132fa1 | ||
|
|
93771ffab8 | ||
|
|
6aff97100f | ||
|
|
6df80fa503 | ||
|
|
31dccd6633 | ||
|
|
71c1d7c540 | ||
|
|
4e5525a48f | ||
|
|
7a84120f7b | ||
|
|
ad7b11e6e8 | ||
|
|
c6af6c0804 | ||
|
|
4d4ee466f7 | ||
|
|
5217d9842b | ||
|
|
04d92740f5 | ||
|
|
a84f762106 | ||
|
|
fca5429ae7 | ||
|
|
39a8f63640 | ||
|
|
42eb14dd77 | ||
|
|
18c2f7847e | ||
|
|
2223662ffd | ||
|
|
69479bdfda | ||
|
|
96a3d315d4 | ||
|
|
a5d1546024 | ||
|
|
f96ad43b1f | ||
|
|
36691e8bdf | ||
|
|
14e70bd718 | ||
|
|
bc0ae352b2 | ||
|
|
ed8460e5be | ||
|
|
2ce78b0993 | ||
|
|
7b65f413e4 | ||
|
|
3a024ca529 | ||
|
|
eb70da83ec | ||
|
|
424c816118 | ||
|
|
8db7bb56b5 | ||
|
|
c03de80e52 | ||
|
|
7245cdf240 | ||
|
|
599d54c5bf | ||
|
|
0f17e7e0b0 | ||
|
|
8e1e0c9b26 | ||
|
|
9965062fe2 | ||
|
|
776284e251 | ||
|
|
64f9f3a248 | ||
|
|
96cec154d4 | ||
|
|
0bc56c8a8f | ||
|
|
ab451a984c | ||
|
|
dabd2130a9 | ||
|
|
763a09e172 | ||
|
|
08fd18e803 | ||
|
|
fff867f637 | ||
|
|
b7cdbec8e2 | ||
|
|
7d5e25d368 | ||
|
|
038eebdeb3 | ||
|
|
58456a7636 | ||
|
|
23ee9bece3 | ||
|
|
6317dd2f81 | ||
|
|
77c19ff152 | ||
|
|
60cbc5b368 | ||
|
|
1fe70f8dba | ||
|
|
08d8a338bc | ||
|
|
1d7efeb561 | ||
|
|
46f5686837 | ||
|
|
8c7cf2015a | ||
|
|
b5246be7fa | ||
|
|
3306a8dfb1 | ||
|
|
51d316b5d2 | ||
|
|
a82e089394 | ||
|
|
c6471625d8 | ||
|
|
7c39176b3a | ||
|
|
1afd3606cc | ||
|
|
9229e9addb | ||
|
|
59d4604ce6 | ||
|
|
e9efa13bc5 | ||
|
|
641b7d87ca | ||
|
|
e646c50019 | ||
|
|
23bab24890 | ||
|
|
39ef771e10 | ||
|
|
19ad278c2d | ||
|
|
9c63885a27 | ||
|
|
78849b02d4 | ||
|
|
342a247efb | ||
|
|
b2aafd696b | ||
|
|
078bb6213d | ||
|
|
c08c023454 | ||
|
|
ccd8d10747 | ||
|
|
b0f7d507d4 | ||
|
|
d3c637c30e | ||
|
|
2daa1d5d1d | ||
|
|
a0137c1ab1 |
79 changed files with 14024 additions and 2162 deletions
5
.clang-format
Normal file
5
.clang-format
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
BasedOnStyle: llvm
|
||||||
|
IndentWidth: 4
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
BreakBeforeBraces: Allman
|
||||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
custom: ["https://www.paypal.me/lundmar"]
|
||||||
7
.github/workflows/codeql-buildscript.sh
vendored
Normal file
7
.github/workflows/codeql-buildscript.sh
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
pip3 install meson -U
|
||||||
|
pip3 install ninja -U
|
||||||
|
sudo apt-get install -y liblua5.2-dev libglib2.0-dev
|
||||||
|
meson setup build
|
||||||
|
meson compile -C build
|
||||||
126
.github/workflows/codeql.yml
vendored
Normal file
126
.github/workflows/codeql.yml
vendored
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main", "master" ]
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
pull_request:
|
||||||
|
branches: '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||||
|
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||||
|
# - https://gh.io/supported-runners-and-hardware-resources
|
||||||
|
# - https://gh.io/using-larger-runners
|
||||||
|
# Consider using larger runners for possible analysis time improvements.
|
||||||
|
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-22.04' }}
|
||||||
|
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ 'cpp' ]
|
||||||
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ]
|
||||||
|
# Use only 'java' to analyze code written in Java, Kotlin or both
|
||||||
|
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
|
||||||
|
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v3
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
|
||||||
|
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||||
|
# queries: security-extended,security-and-quality
|
||||||
|
queries: security-and-quality
|
||||||
|
|
||||||
|
|
||||||
|
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
|
||||||
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
|
#- name: Autobuild
|
||||||
|
# uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||||
|
|
||||||
|
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||||
|
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
./.github/workflows/codeql-buildscript.sh
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v3
|
||||||
|
with:
|
||||||
|
category: "/language:${{matrix.language}}"
|
||||||
|
upload: false
|
||||||
|
id: step1
|
||||||
|
|
||||||
|
# Filter out rules with low severity or high false positve rate
|
||||||
|
# Also filter out warnings in third-party code
|
||||||
|
- name: Filter out unwanted errors and warnings
|
||||||
|
uses: advanced-security/filter-sarif@v1
|
||||||
|
with:
|
||||||
|
patterns: |
|
||||||
|
-**:cpp/path-injection
|
||||||
|
-**:cpp/world-writable-file-creation
|
||||||
|
-**:cpp/poorly-documented-function
|
||||||
|
-**:cpp/potentially-dangerous-function
|
||||||
|
-**:cpp/use-of-goto
|
||||||
|
-**:cpp/integer-multiplication-cast-to-long
|
||||||
|
-**:cpp/comparison-with-wider-type
|
||||||
|
-**:cpp/leap-year/*
|
||||||
|
-**:cpp/ambiguously-signed-bit-field
|
||||||
|
-**:cpp/suspicious-pointer-scaling
|
||||||
|
-**:cpp/suspicious-pointer-scaling-void
|
||||||
|
-**:cpp/unsigned-comparison-zero
|
||||||
|
-**/cmake*/Modules/**
|
||||||
|
input: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
|
||||||
|
output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif
|
||||||
|
|
||||||
|
- name: Upload CodeQL results to code scanning
|
||||||
|
uses: github/codeql-action/upload-sarif@v3
|
||||||
|
with:
|
||||||
|
sarif_file: ${{ steps.step1.outputs.sarif-output }}
|
||||||
|
category: "/language:${{matrix.language}}"
|
||||||
|
|
||||||
|
- name: Upload CodeQL results as an artifact
|
||||||
|
if: success() || failure()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: codeql-results
|
||||||
|
path: ${{ steps.step1.outputs.sarif-output }}
|
||||||
|
retention-days: 5
|
||||||
|
|
||||||
|
- name: Fail if an error is found
|
||||||
|
run: |
|
||||||
|
./.github/workflows/fail_on_error.py \
|
||||||
|
${{ steps.step1.outputs.sarif-output }}/cpp.sarif
|
||||||
34
.github/workflows/fail_on_error.py
vendored
Executable file
34
.github/workflows/fail_on_error.py
vendored
Executable file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Return whether SARIF file contains error-level results
|
||||||
|
def codeql_sarif_contain_error(filename):
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
s = json.load(f)
|
||||||
|
|
||||||
|
for run in s.get('runs', []):
|
||||||
|
rules_metadata = run['tool']['driver']['rules']
|
||||||
|
if not rules_metadata:
|
||||||
|
rules_metadata = run['tool']['extensions'][0]['rules']
|
||||||
|
|
||||||
|
for res in run.get('results', []):
|
||||||
|
if 'ruleIndex' in res:
|
||||||
|
rule_index = res['ruleIndex']
|
||||||
|
elif 'rule' in res and 'index' in res['rule']:
|
||||||
|
rule_index = res['rule']['index']
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
rule_level = rules_metadata[rule_index]['defaultConfiguration']['level']
|
||||||
|
except IndexError as e:
|
||||||
|
print(e, rule_index, len(rules_metadata))
|
||||||
|
else:
|
||||||
|
if rule_level == 'error':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if codeql_sarif_contain_error(sys.argv[1]):
|
||||||
|
sys.exit(1)
|
||||||
30
.github/workflows/macos.yml
vendored
Normal file
30
.github/workflows/macos.yml
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
name: MacOS build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
brew install meson ninja lua
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
meson setup build
|
||||||
|
meson compile -C build --verbose
|
||||||
|
meson install -C build
|
||||||
31
.github/workflows/ubuntu.yml
vendored
Normal file
31
.github/workflows/ubuntu.yml
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
name: Ubuntu build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y bash-completion git meson liblua5.2-dev libglib2.0-dev
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
meson setup build --prefix $HOME/opt/tio
|
||||||
|
meson compile -C build --verbose
|
||||||
|
meson install -C build
|
||||||
26
.gitignore
vendored
26
.gitignore
vendored
|
|
@ -1,24 +1,4 @@
|
||||||
*.tar.xz
|
/build
|
||||||
src/*.o
|
/subprojects/libinih
|
||||||
src/tio
|
|
||||||
Makefile.in
|
|
||||||
Makefile
|
|
||||||
aclocal.m4
|
|
||||||
autom4te.cache/
|
|
||||||
configure
|
|
||||||
compile
|
|
||||||
depcomp
|
|
||||||
install-sh
|
|
||||||
missing
|
|
||||||
src/Makefile.in
|
|
||||||
src/Makefile
|
|
||||||
config.log
|
|
||||||
config.status
|
|
||||||
src/.deps/
|
|
||||||
src/include/config.h
|
|
||||||
src/include/config.h.in
|
|
||||||
src/include/config.h.in~
|
|
||||||
src/include/stamp-h1
|
|
||||||
*.swp
|
*.swp
|
||||||
/tio-*
|
.cache
|
||||||
/src/bash-completion/tio
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
language: c
|
|
||||||
before_script: ./autogen.sh
|
|
||||||
script: ./configure && make
|
|
||||||
2
.typos.toml
Normal file
2
.typos.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[default]
|
||||||
|
extend-ignore-words-re = ["tio"]
|
||||||
57
AUTHORS
57
AUTHORS
|
|
@ -1,6 +1,9 @@
|
||||||
Maintainer:
|
Maintainer:
|
||||||
Martin Lund <martin.lund@keep-it-simple.com>
|
Martin Lund <martin.lund@keep-it-simple.com>
|
||||||
|
|
||||||
|
Co-maintainer:
|
||||||
|
Sylvain LAFRASSE <slymacdev@free.fr>
|
||||||
|
|
||||||
Contributors:
|
Contributors:
|
||||||
Jesper Larsen <knorr.jesper@gmail.com>
|
Jesper Larsen <knorr.jesper@gmail.com>
|
||||||
Jeppe Ledet-Pedersen <jlp@gomspace.com>
|
Jeppe Ledet-Pedersen <jlp@gomspace.com>
|
||||||
|
|
@ -9,5 +12,59 @@ Jakub Wilk <jwilk@jwilk.net>
|
||||||
Martin Hundeboll <martin@hundeboll.net>
|
Martin Hundeboll <martin@hundeboll.net>
|
||||||
Nick Østergaard <oe.nick@gmail.com>
|
Nick Østergaard <oe.nick@gmail.com>
|
||||||
Adam Borowski <kilobyte@angband.pl>
|
Adam Borowski <kilobyte@angband.pl>
|
||||||
|
Robert Scheck <robert@fedoraproject.org>
|
||||||
|
Dmitri Goutnik <dg@syrec.org>
|
||||||
|
Daniel Engberg <daniel.engberg.lists@pyret.net>
|
||||||
|
Petr Vaněk <pv@excello.cz>
|
||||||
|
qianfan Zhao <qianfanguijin@163.com>
|
||||||
|
Henner Zeller <h.zeller@acm.org>
|
||||||
|
Robey Pointer <robey@afero.io>
|
||||||
|
Lars Kellogg-Stedman <lars@oddbit.com>
|
||||||
|
arichi <sergey.korabanov@gmail.com>
|
||||||
|
George Stark <george-u@yandex.com>
|
||||||
|
Erik Moqvist <erik.moqvist@gmail.com>
|
||||||
|
Fabrice Fontaine <fontaine.fabrice@gmail.com>
|
||||||
|
Alban Bedel <alban.bedel@aerq.com>
|
||||||
|
Björn Stenberg <bjorn@haxx.se>
|
||||||
|
Henner Zeller <h.zeller@acm.org>
|
||||||
|
Henrik Brix Andersen <henrik@brixandersen.dk>
|
||||||
|
Mariusz Midor <dexlab@o2.pl>
|
||||||
|
attila-v <attila_v@index.hu>
|
||||||
|
Yin Fengwei <fengwei.yin@intel.com>
|
||||||
|
Liam Beguin <liambeguin@gmail.com>
|
||||||
|
Peter Collingbourne <pcc@google.com>
|
||||||
|
g0mb4 <gomba007@gmail.com>
|
||||||
|
ZeroMemoryEx on GitHub
|
||||||
|
George Joseph <g.devel@wxy78.net>
|
||||||
|
Robert Snell <rcsnell@ncf.ca>
|
||||||
|
Rui Chen <rui@chenrui.dev>
|
||||||
|
Ralph Siemsen <ralphs@netwinder.org>
|
||||||
|
Victor Oliveira <rhapsodyv@gmail.com>
|
||||||
|
Attila Veghelyi <aveghelyi@dension.com>
|
||||||
|
Vyacheslav Patkov <slava@patkov.ru>
|
||||||
|
Bill Hass <billhass@umich.edu>
|
||||||
|
Peter van Dijk <peter@7bits.nl>
|
||||||
|
Braden Young <braden@somewearlabs.com>
|
||||||
|
Wes Koerber <wkoerber@acsd4u.com>
|
||||||
|
HiFiPhile <admin@hifiphile.com>
|
||||||
|
Paul Ruizendaal <pnr@planet.nl>
|
||||||
|
Fredrik Svedberg <fredrik@svedberg.us>
|
||||||
|
Sebastian <sebastian.krahmer@gmail.com>
|
||||||
|
Mingjie Shen <shen497@purdue.edu>
|
||||||
|
Brian <bayuan@purdue.edu>
|
||||||
|
Davis C <davisclaib@gmail.com>
|
||||||
|
KhazAkar <damianzrb@zohomail.eu>
|
||||||
|
Eliot Alan Foss <eliotfoss@gmail.com>
|
||||||
|
Robert Lipe <robertlipe@gpsbabel.org>
|
||||||
|
Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
|
||||||
|
Tomka Gergely <tomkatudor@gmail.com>
|
||||||
|
Steve Marple <stevemarple@googlemail.com>
|
||||||
|
konosubakonoakua <ailike_meow@qq.com>
|
||||||
|
Keith Hill <k_hill@unitronlp.com>
|
||||||
|
Lubov66 <radolevanja@gmail.com>
|
||||||
|
V <v@anomalous.eu>
|
||||||
|
Samuel Holland <samuel@sholland.org>
|
||||||
|
David Ordnung <david.ordnung@googlemail.com>
|
||||||
|
|
||||||
|
|
||||||
Thanks to everyone who has contributed to this project.
|
Thanks to everyone who has contributed to this project.
|
||||||
|
|
|
||||||
339
COPYING
339
COPYING
|
|
@ -1,339 +0,0 @@
|
||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
Version 2, June 1991
|
|
||||||
|
|
||||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The licenses for most software are designed to take away your
|
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
|
||||||
License is intended to guarantee your freedom to share and change free
|
|
||||||
software--to make sure the software is free for all its users. This
|
|
||||||
General Public License applies to most of the Free Software
|
|
||||||
Foundation's software and to any other program whose authors commit to
|
|
||||||
using it. (Some other Free Software Foundation software is covered by
|
|
||||||
the GNU Lesser General Public License instead.) You can apply it to
|
|
||||||
your programs, too.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
|
||||||
have the freedom to distribute copies of free software (and charge for
|
|
||||||
this service if you wish), that you receive source code or can get it
|
|
||||||
if you want it, that you can change the software or use pieces of it
|
|
||||||
in new free programs; and that you know you can do these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to make restrictions that forbid
|
|
||||||
anyone to deny you these rights or to ask you to surrender the rights.
|
|
||||||
These restrictions translate to certain responsibilities for you if you
|
|
||||||
distribute copies of the software, or if you modify it.
|
|
||||||
|
|
||||||
For example, if you distribute copies of such a program, whether
|
|
||||||
gratis or for a fee, you must give the recipients all the rights that
|
|
||||||
you have. You must make sure that they, too, receive or can get the
|
|
||||||
source code. And you must show them these terms so they know their
|
|
||||||
rights.
|
|
||||||
|
|
||||||
We protect your rights with two steps: (1) copyright the software, and
|
|
||||||
(2) offer you this license which gives you legal permission to copy,
|
|
||||||
distribute and/or modify the software.
|
|
||||||
|
|
||||||
Also, for each author's protection and ours, we want to make certain
|
|
||||||
that everyone understands that there is no warranty for this free
|
|
||||||
software. If the software is modified by someone else and passed on, we
|
|
||||||
want its recipients to know that what they have is not the original, so
|
|
||||||
that any problems introduced by others will not reflect on the original
|
|
||||||
authors' reputations.
|
|
||||||
|
|
||||||
Finally, any free program is threatened constantly by software
|
|
||||||
patents. We wish to avoid the danger that redistributors of a free
|
|
||||||
program will individually obtain patent licenses, in effect making the
|
|
||||||
program proprietary. To prevent this, we have made it clear that any
|
|
||||||
patent must be licensed for everyone's free use or not licensed at all.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow.
|
|
||||||
|
|
||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. This License applies to any program or other work which contains
|
|
||||||
a notice placed by the copyright holder saying it may be distributed
|
|
||||||
under the terms of this General Public License. The "Program", below,
|
|
||||||
refers to any such program or work, and a "work based on the Program"
|
|
||||||
means either the Program or any derivative work under copyright law:
|
|
||||||
that is to say, a work containing the Program or a portion of it,
|
|
||||||
either verbatim or with modifications and/or translated into another
|
|
||||||
language. (Hereinafter, translation is included without limitation in
|
|
||||||
the term "modification".) Each licensee is addressed as "you".
|
|
||||||
|
|
||||||
Activities other than copying, distribution and modification are not
|
|
||||||
covered by this License; they are outside its scope. The act of
|
|
||||||
running the Program is not restricted, and the output from the Program
|
|
||||||
is covered only if its contents constitute a work based on the
|
|
||||||
Program (independent of having been made by running the Program).
|
|
||||||
Whether that is true depends on what the Program does.
|
|
||||||
|
|
||||||
1. You may copy and distribute verbatim copies of the Program's
|
|
||||||
source code as you receive it, in any medium, provided that you
|
|
||||||
conspicuously and appropriately publish on each copy an appropriate
|
|
||||||
copyright notice and disclaimer of warranty; keep intact all the
|
|
||||||
notices that refer to this License and to the absence of any warranty;
|
|
||||||
and give any other recipients of the Program a copy of this License
|
|
||||||
along with the Program.
|
|
||||||
|
|
||||||
You may charge a fee for the physical act of transferring a copy, and
|
|
||||||
you may at your option offer warranty protection in exchange for a fee.
|
|
||||||
|
|
||||||
2. You may modify your copy or copies of the Program or any portion
|
|
||||||
of it, thus forming a work based on the Program, and copy and
|
|
||||||
distribute such modifications or work under the terms of Section 1
|
|
||||||
above, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) You must cause the modified files to carry prominent notices
|
|
||||||
stating that you changed the files and the date of any change.
|
|
||||||
|
|
||||||
b) You must cause any work that you distribute or publish, that in
|
|
||||||
whole or in part contains or is derived from the Program or any
|
|
||||||
part thereof, to be licensed as a whole at no charge to all third
|
|
||||||
parties under the terms of this License.
|
|
||||||
|
|
||||||
c) If the modified program normally reads commands interactively
|
|
||||||
when run, you must cause it, when started running for such
|
|
||||||
interactive use in the most ordinary way, to print or display an
|
|
||||||
announcement including an appropriate copyright notice and a
|
|
||||||
notice that there is no warranty (or else, saying that you provide
|
|
||||||
a warranty) and that users may redistribute the program under
|
|
||||||
these conditions, and telling the user how to view a copy of this
|
|
||||||
License. (Exception: if the Program itself is interactive but
|
|
||||||
does not normally print such an announcement, your work based on
|
|
||||||
the Program is not required to print an announcement.)
|
|
||||||
|
|
||||||
These requirements apply to the modified work as a whole. If
|
|
||||||
identifiable sections of that work are not derived from the Program,
|
|
||||||
and can be reasonably considered independent and separate works in
|
|
||||||
themselves, then this License, and its terms, do not apply to those
|
|
||||||
sections when you distribute them as separate works. But when you
|
|
||||||
distribute the same sections as part of a whole which is a work based
|
|
||||||
on the Program, the distribution of the whole must be on the terms of
|
|
||||||
this License, whose permissions for other licensees extend to the
|
|
||||||
entire whole, and thus to each and every part regardless of who wrote it.
|
|
||||||
|
|
||||||
Thus, it is not the intent of this section to claim rights or contest
|
|
||||||
your rights to work written entirely by you; rather, the intent is to
|
|
||||||
exercise the right to control the distribution of derivative or
|
|
||||||
collective works based on the Program.
|
|
||||||
|
|
||||||
In addition, mere aggregation of another work not based on the Program
|
|
||||||
with the Program (or with a work based on the Program) on a volume of
|
|
||||||
a storage or distribution medium does not bring the other work under
|
|
||||||
the scope of this License.
|
|
||||||
|
|
||||||
3. You may copy and distribute the Program (or a work based on it,
|
|
||||||
under Section 2) in object code or executable form under the terms of
|
|
||||||
Sections 1 and 2 above provided that you also do one of the following:
|
|
||||||
|
|
||||||
a) Accompany it with the complete corresponding machine-readable
|
|
||||||
source code, which must be distributed under the terms of Sections
|
|
||||||
1 and 2 above on a medium customarily used for software interchange; or,
|
|
||||||
|
|
||||||
b) Accompany it with a written offer, valid for at least three
|
|
||||||
years, to give any third party, for a charge no more than your
|
|
||||||
cost of physically performing source distribution, a complete
|
|
||||||
machine-readable copy of the corresponding source code, to be
|
|
||||||
distributed under the terms of Sections 1 and 2 above on a medium
|
|
||||||
customarily used for software interchange; or,
|
|
||||||
|
|
||||||
c) Accompany it with the information you received as to the offer
|
|
||||||
to distribute corresponding source code. (This alternative is
|
|
||||||
allowed only for noncommercial distribution and only if you
|
|
||||||
received the program in object code or executable form with such
|
|
||||||
an offer, in accord with Subsection b above.)
|
|
||||||
|
|
||||||
The source code for a work means the preferred form of the work for
|
|
||||||
making modifications to it. For an executable work, complete source
|
|
||||||
code means all the source code for all modules it contains, plus any
|
|
||||||
associated interface definition files, plus the scripts used to
|
|
||||||
control compilation and installation of the executable. However, as a
|
|
||||||
special exception, the source code distributed need not include
|
|
||||||
anything that is normally distributed (in either source or binary
|
|
||||||
form) with the major components (compiler, kernel, and so on) of the
|
|
||||||
operating system on which the executable runs, unless that component
|
|
||||||
itself accompanies the executable.
|
|
||||||
|
|
||||||
If distribution of executable or object code is made by offering
|
|
||||||
access to copy from a designated place, then offering equivalent
|
|
||||||
access to copy the source code from the same place counts as
|
|
||||||
distribution of the source code, even though third parties are not
|
|
||||||
compelled to copy the source along with the object code.
|
|
||||||
|
|
||||||
4. You may not copy, modify, sublicense, or distribute the Program
|
|
||||||
except as expressly provided under this License. Any attempt
|
|
||||||
otherwise to copy, modify, sublicense or distribute the Program is
|
|
||||||
void, and will automatically terminate your rights under this License.
|
|
||||||
However, parties who have received copies, or rights, from you under
|
|
||||||
this License will not have their licenses terminated so long as such
|
|
||||||
parties remain in full compliance.
|
|
||||||
|
|
||||||
5. You are not required to accept this License, since you have not
|
|
||||||
signed it. However, nothing else grants you permission to modify or
|
|
||||||
distribute the Program or its derivative works. These actions are
|
|
||||||
prohibited by law if you do not accept this License. Therefore, by
|
|
||||||
modifying or distributing the Program (or any work based on the
|
|
||||||
Program), you indicate your acceptance of this License to do so, and
|
|
||||||
all its terms and conditions for copying, distributing or modifying
|
|
||||||
the Program or works based on it.
|
|
||||||
|
|
||||||
6. Each time you redistribute the Program (or any work based on the
|
|
||||||
Program), the recipient automatically receives a license from the
|
|
||||||
original licensor to copy, distribute or modify the Program subject to
|
|
||||||
these terms and conditions. You may not impose any further
|
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
|
||||||
You are not responsible for enforcing compliance by third parties to
|
|
||||||
this License.
|
|
||||||
|
|
||||||
7. If, as a consequence of a court judgment or allegation of patent
|
|
||||||
infringement or for any other reason (not limited to patent issues),
|
|
||||||
conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot
|
|
||||||
distribute so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you
|
|
||||||
may not distribute the Program at all. For example, if a patent
|
|
||||||
license would not permit royalty-free redistribution of the Program by
|
|
||||||
all those who receive copies directly or indirectly through you, then
|
|
||||||
the only way you could satisfy both it and this License would be to
|
|
||||||
refrain entirely from distribution of the Program.
|
|
||||||
|
|
||||||
If any portion of this section is held invalid or unenforceable under
|
|
||||||
any particular circumstance, the balance of the section is intended to
|
|
||||||
apply and the section as a whole is intended to apply in other
|
|
||||||
circumstances.
|
|
||||||
|
|
||||||
It is not the purpose of this section to induce you to infringe any
|
|
||||||
patents or other property right claims or to contest validity of any
|
|
||||||
such claims; this section has the sole purpose of protecting the
|
|
||||||
integrity of the free software distribution system, which is
|
|
||||||
implemented by public license practices. Many people have made
|
|
||||||
generous contributions to the wide range of software distributed
|
|
||||||
through that system in reliance on consistent application of that
|
|
||||||
system; it is up to the author/donor to decide if he or she is willing
|
|
||||||
to distribute software through any other system and a licensee cannot
|
|
||||||
impose that choice.
|
|
||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
|
||||||
be a consequence of the rest of this License.
|
|
||||||
|
|
||||||
8. If the distribution and/or use of the Program is restricted in
|
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
|
||||||
original copyright holder who places the Program under this License
|
|
||||||
may add an explicit geographical distribution limitation excluding
|
|
||||||
those countries, so that distribution is permitted only in or among
|
|
||||||
countries not thus excluded. In such case, this License incorporates
|
|
||||||
the limitation as if written in the body of this License.
|
|
||||||
|
|
||||||
9. The Free Software Foundation may publish revised and/or new versions
|
|
||||||
of the General Public License from time to time. Such new versions will
|
|
||||||
be similar in spirit to the present version, but may differ in detail to
|
|
||||||
address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the Program
|
|
||||||
specifies a version number of this License which applies to it and "any
|
|
||||||
later version", you have the option of following the terms and conditions
|
|
||||||
either of that version or of any later version published by the Free
|
|
||||||
Software Foundation. If the Program does not specify a version number of
|
|
||||||
this License, you may choose any version ever published by the Free Software
|
|
||||||
Foundation.
|
|
||||||
|
|
||||||
10. If you wish to incorporate parts of the Program into other free
|
|
||||||
programs whose distribution conditions are different, write to the author
|
|
||||||
to ask for permission. For software which is copyrighted by the Free
|
|
||||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
|
||||||
make exceptions for this. Our decision will be guided by the two goals
|
|
||||||
of preserving the free status of all derivatives of our free software and
|
|
||||||
of promoting the sharing and reuse of software generally.
|
|
||||||
|
|
||||||
NO WARRANTY
|
|
||||||
|
|
||||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
|
||||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
|
||||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
|
||||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
|
||||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
|
||||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
|
||||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
|
||||||
REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
|
||||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
|
||||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
|
||||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
|
||||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
|
||||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
|
||||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
|
||||||
POSSIBILITY OF SUCH DAMAGES.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
|
||||||
possible use to the public, the best way to achieve this is to make it
|
|
||||||
free software which everyone can redistribute and change under these terms.
|
|
||||||
|
|
||||||
To do so, attach the following notices to the program. It is safest
|
|
||||||
to attach them to the start of each source file to most effectively
|
|
||||||
convey the exclusion of warranty; and each file should have at least
|
|
||||||
the "copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
This program 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.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
If the program is interactive, make it output a short notice like this
|
|
||||||
when it starts in an interactive mode:
|
|
||||||
|
|
||||||
Gnomovision version 69, Copyright (C) year name of author
|
|
||||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
|
||||||
This is free software, and you are welcome to redistribute it
|
|
||||||
under certain conditions; type `show c' for details.
|
|
||||||
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
|
||||||
parts of the General Public License. Of course, the commands you use may
|
|
||||||
be called something other than `show w' and `show c'; they could even be
|
|
||||||
mouse-clicks or menu items--whatever suits your program.
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or your
|
|
||||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
|
||||||
necessary. Here is a sample; alter the names:
|
|
||||||
|
|
||||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
|
||||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
|
||||||
|
|
||||||
<signature of Ty Coon>, 1 April 1989
|
|
||||||
Ty Coon, President of Vice
|
|
||||||
|
|
||||||
This General Public License does not permit incorporating your program into
|
|
||||||
proprietary programs. If your program is a subroutine library, you may
|
|
||||||
consider it more useful to permit linking proprietary applications with the
|
|
||||||
library. If this is what you want to do, use the GNU Lesser General
|
|
||||||
Public License instead of this License.
|
|
||||||
492
ChangeLog
492
ChangeLog
|
|
@ -1,492 +0,0 @@
|
||||||
|
|
||||||
=== tio v1.25 ===
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.24:
|
|
||||||
|
|
||||||
* Fix error applying new stdout settings
|
|
||||||
|
|
||||||
On Fedora 26 tio will quit with the following error message:
|
|
||||||
|
|
||||||
"Error: Could not apply new stdout settings (Invalid argument)"
|
|
||||||
|
|
||||||
In case of Fedora, it turns out that the new stdout settings used are a
|
|
||||||
bit too agressive because an empty termios structure is used. To remedy
|
|
||||||
this we reuse the existing stdout settings and only reconfigure the
|
|
||||||
specific options we need to make a "raw" stdout configuration.
|
|
||||||
|
|
||||||
* Remove unused pkgconfig in configure
|
|
||||||
|
|
||||||
* Code cleanup
|
|
||||||
|
|
||||||
Remove unused variable.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.23:
|
|
||||||
|
|
||||||
* Optimize clear screen command
|
|
||||||
|
|
||||||
Replaced system call with inline ANSI/VT100 clear screen code sequence
|
|
||||||
|
|
||||||
* Fix bash completion installation
|
|
||||||
|
|
||||||
Fixed the configure script to avoid that the bash completion script gets
|
|
||||||
installed outside of the prefix location. The default install location
|
|
||||||
is now $prefix/share/bash-completion/completions.
|
|
||||||
|
|
||||||
Use the configure option '--with-bash-completion-dir=PATH' if you need
|
|
||||||
to install the bash completion script elsewhere.
|
|
||||||
|
|
||||||
Jakub Wilk:
|
|
||||||
|
|
||||||
* Add missing commas in conditional sentences
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.22:
|
|
||||||
|
|
||||||
* Update copyright headers
|
|
||||||
|
|
||||||
Jakub Wilk:
|
|
||||||
|
|
||||||
* Fix typos
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.21:
|
|
||||||
|
|
||||||
* Update man page date
|
|
||||||
|
|
||||||
* Update copyright year
|
|
||||||
|
|
||||||
* Code cleanup
|
|
||||||
|
|
||||||
* Update README and man page
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.20:
|
|
||||||
|
|
||||||
* Add support for hexadecimal mode
|
|
||||||
|
|
||||||
A new key command 'ctrl-t h' is introduced which toggles between
|
|
||||||
hexadecimal mode and normal mode. When in hexadecimal mode data received
|
|
||||||
will be printed in hexadecimal.
|
|
||||||
|
|
||||||
* Do not distribute src/bash_completion/tio
|
|
||||||
|
|
||||||
Since the bash completion tio script is now autogenerated from tio.in it
|
|
||||||
should not be distributed in the tarball.
|
|
||||||
|
|
||||||
* Add missing forward flag
|
|
||||||
|
|
||||||
* Update AUTHORS file
|
|
||||||
|
|
||||||
Adam Borowski:
|
|
||||||
|
|
||||||
* 'ctrl-t b' to send serial break.
|
|
||||||
|
|
||||||
Jakub Wilk:
|
|
||||||
|
|
||||||
* Removed git commit references from ChangeLog
|
|
||||||
|
|
||||||
ChangeLog is primary useful for users who don't have the git repository
|
|
||||||
at hand.
|
|
||||||
|
|
||||||
Replace git commit references with version numbers; or if the change
|
|
||||||
only cleans up another change with no release in between, remove the
|
|
||||||
changelog item completely.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.19:
|
|
||||||
|
|
||||||
* Added more error handling of terminal calls
|
|
||||||
|
|
||||||
Also removed duplicate terminal flushing calls.
|
|
||||||
|
|
||||||
* Revert "Added support for non-standard baud rates"
|
|
||||||
|
|
||||||
This reverts a change made in v1.18.
|
|
||||||
|
|
||||||
Reverting because supporting non-standard or arbitrary baud rates is
|
|
||||||
troublesome because the c library provides no means of doing so and even
|
|
||||||
if bare metal linux kernel interface is used it will not work on all
|
|
||||||
Linux kernels version.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.18:
|
|
||||||
|
|
||||||
* Rearranged key commands
|
|
||||||
|
|
||||||
Rearranged the key commands:
|
|
||||||
|
|
||||||
ctrl-t c (clear screen) is now
|
|
||||||
ctrl-t l which is similar to the well known shell ctrl-l
|
|
||||||
|
|
||||||
ctrl-t i (show settings information) is now
|
|
||||||
ctrl-t c (show configuration)
|
|
||||||
|
|
||||||
Updated man page accordingly.
|
|
||||||
|
|
||||||
* Added "ctrl-t c" key command to clear screen
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.17:
|
|
||||||
|
|
||||||
* Updated man page
|
|
||||||
|
|
||||||
* Added support for non-standard baud rates
|
|
||||||
|
|
||||||
Only enabled when possible, that is, when the BOTHER definition is
|
|
||||||
available.
|
|
||||||
|
|
||||||
It is untested but it should work as described here:
|
|
||||||
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=683826
|
|
||||||
|
|
||||||
Some Cypress USB<->serial devices supposedly supports arbitrary speeds.
|
|
||||||
|
|
||||||
* Generate baudrate switch cases based on detection
|
|
||||||
|
|
||||||
Support a single source of baud rate configuration as discussed in
|
|
||||||
https://github.com/tio/tio/issues/45 .
|
|
||||||
|
|
||||||
To do so, autogeneration of the switch cases which do the baud rate
|
|
||||||
option value check and configuration/conversion in tty_configure() is
|
|
||||||
introduced via a single macro.
|
|
||||||
|
|
||||||
Just to be safe, this change also enables configure detection of all
|
|
||||||
baud rates, including the ones previously assumed supported by most/all
|
|
||||||
systems (POSIX).
|
|
||||||
|
|
||||||
* Minor cleanup
|
|
||||||
|
|
||||||
* Exit when not a tty device in autoconnect mode
|
|
||||||
|
|
||||||
Jakub Wilk:
|
|
||||||
|
|
||||||
* Added non-standard baud rates that are defined on FreeBSD
|
|
||||||
|
|
||||||
* Capitalized "GitHub" in README
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.16:
|
|
||||||
|
|
||||||
* Compacted tty_configure() a bit
|
|
||||||
|
|
||||||
* Fixed automatic baud rate enablement
|
|
||||||
|
|
||||||
* Minor cleanups
|
|
||||||
|
|
||||||
* Added autodetection of available baud rates
|
|
||||||
|
|
||||||
Various platforms support different baud rates.
|
|
||||||
|
|
||||||
To avoid adding platform specific handling generic baud rate detection
|
|
||||||
tests are introduced in the configure script. Successfully detected baud
|
|
||||||
rates are automatically enabled. This applies to both the C code and the
|
|
||||||
bash completion script.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
Baud rates below 57600 are defined by POSIX-1 and supported by most
|
|
||||||
platforms so only baud rate 57600 and above are tested.
|
|
||||||
|
|
||||||
* Updated bash-completion
|
|
||||||
|
|
||||||
* Fixed printf() format type
|
|
||||||
|
|
||||||
* Added Travis build configuration
|
|
||||||
|
|
||||||
Jakub Wilk:
|
|
||||||
|
|
||||||
* Generated bash completion at configure time
|
|
||||||
|
|
||||||
* Reduce code duplication in baud rate detection
|
|
||||||
|
|
||||||
* Add support for baud rates 200 and 1800
|
|
||||||
|
|
||||||
* Fixed baudrate type
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.15:
|
|
||||||
|
|
||||||
* Updated man page
|
|
||||||
|
|
||||||
* Updated README
|
|
||||||
|
|
||||||
* Removed obsolete packaging files
|
|
||||||
|
|
||||||
* Removed use of deprecated bzero()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.14:
|
|
||||||
|
|
||||||
* Removed + to remove potential confusion
|
|
||||||
|
|
||||||
* Added input digit checks
|
|
||||||
|
|
||||||
* Fixed license string
|
|
||||||
|
|
||||||
* Introduced tty_configure()
|
|
||||||
|
|
||||||
Moved tty configuration actions to tty_configure() in tty.c. This way
|
|
||||||
options.c is strictly about parsing options nothing else.
|
|
||||||
|
|
||||||
* Function names cleanup
|
|
||||||
|
|
||||||
* Updated AUTHORS file
|
|
||||||
|
|
||||||
Added Nick who created the new tio package for Arch Linux.
|
|
||||||
|
|
||||||
* Fixed tx/rx counters type
|
|
||||||
|
|
||||||
Jakob Haufe:
|
|
||||||
|
|
||||||
* Include config.h before standard headers
|
|
||||||
|
|
||||||
Large file support was meant to be enabled in v1.11.
|
|
||||||
This change enables it for real.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.13:
|
|
||||||
|
|
||||||
* Fixed tio_printf macro
|
|
||||||
|
|
||||||
* Fixed launch hints
|
|
||||||
|
|
||||||
Fixed launch hints not being printed in no autoconnect mode.
|
|
||||||
|
|
||||||
* Added 'ctrl-t ?' to list available commands
|
|
||||||
|
|
||||||
* Fixed log mechanism
|
|
||||||
|
|
||||||
To avoid echoing only log what is received from tty device.
|
|
||||||
|
|
||||||
* Improved tio output
|
|
||||||
|
|
||||||
Added titles and indentation to commands output for clearer separation
|
|
||||||
when firing commands repeatedly.
|
|
||||||
|
|
||||||
Also added print of tio version and quit command hint at launch.
|
|
||||||
|
|
||||||
* Cleaned up tio print mechanism
|
|
||||||
|
|
||||||
Jakub Wilk:
|
|
||||||
|
|
||||||
* Fixed grammar
|
|
||||||
|
|
||||||
"allow" is a transitive verb, which requires an object,
|
|
||||||
so "allow to <verb>" is ungrammatical.
|
|
||||||
|
|
||||||
* Fixed typo
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.12:
|
|
||||||
|
|
||||||
* Fixed some error prints
|
|
||||||
|
|
||||||
* Fixed error printing for no autoconnect mode
|
|
||||||
|
|
||||||
Always print errors but only print silent errors when in no autoconnect
|
|
||||||
mode.
|
|
||||||
|
|
||||||
* Added key command for showing session settings
|
|
||||||
|
|
||||||
A new key command "ctrl-t i" is added to allow the user to display the
|
|
||||||
various session settings information (baudrate, databits, log file, etc.).
|
|
||||||
|
|
||||||
This is useful in case you have a running session but have forgotten
|
|
||||||
what the settings are.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.11:
|
|
||||||
|
|
||||||
* Consolidated command key handling
|
|
||||||
|
|
||||||
* Moved delay mechanism into separate function
|
|
||||||
|
|
||||||
* Retired obsolete usleep()
|
|
||||||
|
|
||||||
Replaced with nanosleep()
|
|
||||||
|
|
||||||
* Added simple tx/rx statistics command (ctrl-t s)
|
|
||||||
|
|
||||||
To display the total number of bytes transmitted/received simply perform the
|
|
||||||
'ctrl-t s' command sequence.
|
|
||||||
|
|
||||||
This feature can be useful when eg. trying to detect non-printable
|
|
||||||
characters.
|
|
||||||
|
|
||||||
* Further simplification of key handling
|
|
||||||
|
|
||||||
Changed so that the "ctrl-t ctrl-t" sequence is now simply "ctrl-t t" to
|
|
||||||
send the ctrl-t key code. This is inspired by screen which does similar
|
|
||||||
to send its command key code (ctrl-a a).
|
|
||||||
|
|
||||||
This change also eases adding new key commands if needed.
|
|
||||||
|
|
||||||
Updated man page accordingly.
|
|
||||||
|
|
||||||
* Cleaned up and simplified key handling
|
|
||||||
|
|
||||||
Jakub Wilk:
|
|
||||||
|
|
||||||
* Insert output delay only if something was output
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.10:
|
|
||||||
|
|
||||||
* Enabled large file support (LFS)
|
|
||||||
|
|
||||||
Added autotools AC_SYS_LARGEFILE to support 64 bit file size handling.
|
|
||||||
|
|
||||||
* Updated tio title
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.9:
|
|
||||||
|
|
||||||
* Introduced lock on device file
|
|
||||||
|
|
||||||
Tio will now test for and obtain an advisory lock on the tty device file
|
|
||||||
to prevent starting multiple sessions on the same tty device.
|
|
||||||
|
|
||||||
* Updated AUTHORS
|
|
||||||
|
|
||||||
Jakub Wilk:
|
|
||||||
|
|
||||||
* Treat EOF on stdin as error
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.8:
|
|
||||||
|
|
||||||
* Cleanup of error handling
|
|
||||||
|
|
||||||
Introduced consistent way of handling errors and printing error messages.
|
|
||||||
|
|
||||||
Also upgraded some warnings to errors.
|
|
||||||
|
|
||||||
* Updated localtime() error message
|
|
||||||
|
|
||||||
* Cleanup
|
|
||||||
|
|
||||||
Jakub Wilk:
|
|
||||||
|
|
||||||
* Fix error handling for select()
|
|
||||||
|
|
||||||
Previously the error handling code for select() was unreachable.
|
|
||||||
|
|
||||||
* Removed unneeded quotes from AM_CFLAGS
|
|
||||||
|
|
||||||
* Expanded tabs
|
|
||||||
|
|
||||||
* Fixed setting "tainted"
|
|
||||||
|
|
||||||
Set "tainted" if and only if any character was read from the device.
|
|
||||||
|
|
||||||
Ctrl-t is no longer sent to the device on exit, so the trick to avoid
|
|
||||||
its echo is not necessary.
|
|
||||||
|
|
||||||
Characters read from stdin don't directly affect output, so they
|
|
||||||
shouldn't enable "tainted".
|
|
||||||
|
|
||||||
* Used \r in color_printf()
|
|
||||||
|
|
||||||
\033[300D is an unusual way to move the cursor back to column 1.
|
|
||||||
Use straightforward \r instead.
|
|
||||||
|
|
||||||
* Added missing \r\n to warning messages
|
|
||||||
|
|
||||||
\n alone is not enough, because the terminal is in raw mode.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.7:
|
|
||||||
|
|
||||||
* Fixed enablement of compiler warnings
|
|
||||||
|
|
||||||
* Fixed log_open() prototype
|
|
||||||
|
|
||||||
* Fixed index error wrt ctrl-t detection
|
|
||||||
|
|
||||||
* Fixed handling of ctrl-t
|
|
||||||
|
|
||||||
Before, when exercising the quit key sequence (ctrl-t + q) the ctrl-t code
|
|
||||||
(0x14) would be sent.
|
|
||||||
|
|
||||||
This is now fixed so that it is not sent.
|
|
||||||
|
|
||||||
However, in case it is needed to send ctrl-t to the device it is possible by
|
|
||||||
simply repeating the ctrl-t.
|
|
||||||
|
|
||||||
Meaning, ctrl-t + ctrl-t = ctrl-t sent to device.
|
|
||||||
|
|
||||||
* Improved error handling
|
|
||||||
|
|
||||||
Fixes a memory leak and avoids aggressive busy looping when problems
|
|
||||||
accessing tty device.
|
|
||||||
|
|
||||||
* Removed redundant log_close() call
|
|
||||||
|
|
||||||
* Enabled compiler warnings
|
|
||||||
|
|
||||||
Jakub Wilk:
|
|
||||||
|
|
||||||
* Stopped copying arguments to fixed-size buffers
|
|
||||||
|
|
||||||
Don't needlessly copy command-line arguments into fixed-size buffers.
|
|
||||||
|
|
||||||
Previously the program crashed if an overlong pathname was provided on
|
|
||||||
the command line. Also, some systems (such as GNU Hurd) don't define
|
|
||||||
MAXPATHLEN at all.
|
|
||||||
|
|
||||||
* Added const to log_open() prototype
|
|
||||||
|
|
||||||
* Completed the ^g to ^t transition
|
|
||||||
|
|
||||||
In v1.7 the escape key was changed from ^g to ^t, but some
|
|
||||||
code and comments still referred to the old key.
|
|
||||||
|
|
||||||
* Used HTTPS for tio.github.io
|
|
||||||
|
|
||||||
* Man page beautification
|
|
||||||
|
|
||||||
* Bumped date in man page
|
|
||||||
|
|
||||||
* Improve man page formatting
|
|
||||||
|
|
||||||
Use regular font for metacharacters such as "[]", "," or "|";
|
|
||||||
use italic font for metavariables.
|
|
||||||
|
|
||||||
* Fixed hyphen vs minus vs em-dash confusion in man page
|
|
||||||
|
|
||||||
- prints as hyphen;
|
|
||||||
\- prints as minus sign;
|
|
||||||
\em prints as em-dash.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changes since tio v1.6:
|
|
||||||
|
|
||||||
* Changed escape key from ^g to ^t
|
|
||||||
|
|
||||||
After renaming to "tio" it makes sense to change the escape key
|
|
||||||
accordingly. Hence, the new escape key is ^t.
|
|
||||||
|
|
||||||
Meaning, in session, its now ctrl-t + q to quit.
|
|
||||||
|
|
||||||
Jakub Wilk:
|
|
||||||
|
|
||||||
* Fixed silly "tio or tio" in man page
|
|
||||||
|
|
||||||
* Fixed typo
|
|
||||||
370
INSTALL
370
INSTALL
|
|
@ -1,370 +0,0 @@
|
||||||
Installation Instructions
|
|
||||||
*************************
|
|
||||||
|
|
||||||
Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
|
|
||||||
Inc.
|
|
||||||
|
|
||||||
Copying and distribution of this file, with or without modification,
|
|
||||||
are permitted in any medium without royalty provided the copyright
|
|
||||||
notice and this notice are preserved. This file is offered as-is,
|
|
||||||
without warranty of any kind.
|
|
||||||
|
|
||||||
Basic Installation
|
|
||||||
==================
|
|
||||||
|
|
||||||
Briefly, the shell command `./configure && make && make install'
|
|
||||||
should configure, build, and install this package. The following
|
|
||||||
more-detailed instructions are generic; see the `README' file for
|
|
||||||
instructions specific to this package. Some packages provide this
|
|
||||||
`INSTALL' file but do not implement all of the features documented
|
|
||||||
below. The lack of an optional feature in a given package is not
|
|
||||||
necessarily a bug. More recommendations for GNU packages can be found
|
|
||||||
in *note Makefile Conventions: (standards)Makefile Conventions.
|
|
||||||
|
|
||||||
The `configure' shell script attempts to guess correct values for
|
|
||||||
various system-dependent variables used during compilation. It uses
|
|
||||||
those values to create a `Makefile' in each directory of the package.
|
|
||||||
It may also create one or more `.h' files containing system-dependent
|
|
||||||
definitions. Finally, it creates a shell script `config.status' that
|
|
||||||
you can run in the future to recreate the current configuration, and a
|
|
||||||
file `config.log' containing compiler output (useful mainly for
|
|
||||||
debugging `configure').
|
|
||||||
|
|
||||||
It can also use an optional file (typically called `config.cache'
|
|
||||||
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
|
||||||
the results of its tests to speed up reconfiguring. Caching is
|
|
||||||
disabled by default to prevent problems with accidental use of stale
|
|
||||||
cache files.
|
|
||||||
|
|
||||||
If you need to do unusual things to compile the package, please try
|
|
||||||
to figure out how `configure' could check whether to do them, and mail
|
|
||||||
diffs or instructions to the address given in the `README' so they can
|
|
||||||
be considered for the next release. If you are using the cache, and at
|
|
||||||
some point `config.cache' contains results you don't want to keep, you
|
|
||||||
may remove or edit it.
|
|
||||||
|
|
||||||
The file `configure.ac' (or `configure.in') is used to create
|
|
||||||
`configure' by a program called `autoconf'. You need `configure.ac' if
|
|
||||||
you want to change it or regenerate `configure' using a newer version
|
|
||||||
of `autoconf'.
|
|
||||||
|
|
||||||
The simplest way to compile this package is:
|
|
||||||
|
|
||||||
1. `cd' to the directory containing the package's source code and type
|
|
||||||
`./configure' to configure the package for your system.
|
|
||||||
|
|
||||||
Running `configure' might take a while. While running, it prints
|
|
||||||
some messages telling which features it is checking for.
|
|
||||||
|
|
||||||
2. Type `make' to compile the package.
|
|
||||||
|
|
||||||
3. Optionally, type `make check' to run any self-tests that come with
|
|
||||||
the package, generally using the just-built uninstalled binaries.
|
|
||||||
|
|
||||||
4. Type `make install' to install the programs and any data files and
|
|
||||||
documentation. When installing into a prefix owned by root, it is
|
|
||||||
recommended that the package be configured and built as a regular
|
|
||||||
user, and only the `make install' phase executed with root
|
|
||||||
privileges.
|
|
||||||
|
|
||||||
5. Optionally, type `make installcheck' to repeat any self-tests, but
|
|
||||||
this time using the binaries in their final installed location.
|
|
||||||
This target does not install anything. Running this target as a
|
|
||||||
regular user, particularly if the prior `make install' required
|
|
||||||
root privileges, verifies that the installation completed
|
|
||||||
correctly.
|
|
||||||
|
|
||||||
6. You can remove the program binaries and object files from the
|
|
||||||
source code directory by typing `make clean'. To also remove the
|
|
||||||
files that `configure' created (so you can compile the package for
|
|
||||||
a different kind of computer), type `make distclean'. There is
|
|
||||||
also a `make maintainer-clean' target, but that is intended mainly
|
|
||||||
for the package's developers. If you use it, you may have to get
|
|
||||||
all sorts of other programs in order to regenerate files that came
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
7. Often, you can also type `make uninstall' to remove the installed
|
|
||||||
files again. In practice, not all packages have tested that
|
|
||||||
uninstallation works correctly, even though it is required by the
|
|
||||||
GNU Coding Standards.
|
|
||||||
|
|
||||||
8. Some packages, particularly those that use Automake, provide `make
|
|
||||||
distcheck', which can by used by developers to test that all other
|
|
||||||
targets like `make install' and `make uninstall' work correctly.
|
|
||||||
This target is generally not run by end users.
|
|
||||||
|
|
||||||
Compilers and Options
|
|
||||||
=====================
|
|
||||||
|
|
||||||
Some systems require unusual options for compilation or linking that
|
|
||||||
the `configure' script does not know about. Run `./configure --help'
|
|
||||||
for details on some of the pertinent environment variables.
|
|
||||||
|
|
||||||
You can give `configure' initial values for configuration parameters
|
|
||||||
by setting variables in the command line or in the environment. Here
|
|
||||||
is an example:
|
|
||||||
|
|
||||||
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
|
||||||
|
|
||||||
*Note Defining Variables::, for more details.
|
|
||||||
|
|
||||||
Compiling For Multiple Architectures
|
|
||||||
====================================
|
|
||||||
|
|
||||||
You can compile the package for more than one kind of computer at the
|
|
||||||
same time, by placing the object files for each architecture in their
|
|
||||||
own directory. To do this, you can use GNU `make'. `cd' to the
|
|
||||||
directory where you want the object files and executables to go and run
|
|
||||||
the `configure' script. `configure' automatically checks for the
|
|
||||||
source code in the directory that `configure' is in and in `..'. This
|
|
||||||
is known as a "VPATH" build.
|
|
||||||
|
|
||||||
With a non-GNU `make', it is safer to compile the package for one
|
|
||||||
architecture at a time in the source code directory. After you have
|
|
||||||
installed the package for one architecture, use `make distclean' before
|
|
||||||
reconfiguring for another architecture.
|
|
||||||
|
|
||||||
On MacOS X 10.5 and later systems, you can create libraries and
|
|
||||||
executables that work on multiple system types--known as "fat" or
|
|
||||||
"universal" binaries--by specifying multiple `-arch' options to the
|
|
||||||
compiler but only a single `-arch' option to the preprocessor. Like
|
|
||||||
this:
|
|
||||||
|
|
||||||
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
|
||||||
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
|
||||||
CPP="gcc -E" CXXCPP="g++ -E"
|
|
||||||
|
|
||||||
This is not guaranteed to produce working output in all cases, you
|
|
||||||
may have to build one architecture at a time and combine the results
|
|
||||||
using the `lipo' tool if you have problems.
|
|
||||||
|
|
||||||
Installation Names
|
|
||||||
==================
|
|
||||||
|
|
||||||
By default, `make install' installs the package's commands under
|
|
||||||
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
|
||||||
can specify an installation prefix other than `/usr/local' by giving
|
|
||||||
`configure' the option `--prefix=PREFIX', where PREFIX must be an
|
|
||||||
absolute file name.
|
|
||||||
|
|
||||||
You can specify separate installation prefixes for
|
|
||||||
architecture-specific files and architecture-independent files. If you
|
|
||||||
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
|
||||||
PREFIX as the prefix for installing programs and libraries.
|
|
||||||
Documentation and other data files still use the regular prefix.
|
|
||||||
|
|
||||||
In addition, if you use an unusual directory layout you can give
|
|
||||||
options like `--bindir=DIR' to specify different values for particular
|
|
||||||
kinds of files. Run `configure --help' for a list of the directories
|
|
||||||
you can set and what kinds of files go in them. In general, the
|
|
||||||
default for these options is expressed in terms of `${prefix}', so that
|
|
||||||
specifying just `--prefix' will affect all of the other directory
|
|
||||||
specifications that were not explicitly provided.
|
|
||||||
|
|
||||||
The most portable way to affect installation locations is to pass the
|
|
||||||
correct locations to `configure'; however, many packages provide one or
|
|
||||||
both of the following shortcuts of passing variable assignments to the
|
|
||||||
`make install' command line to change installation locations without
|
|
||||||
having to reconfigure or recompile.
|
|
||||||
|
|
||||||
The first method involves providing an override variable for each
|
|
||||||
affected directory. For example, `make install
|
|
||||||
prefix=/alternate/directory' will choose an alternate location for all
|
|
||||||
directory configuration variables that were expressed in terms of
|
|
||||||
`${prefix}'. Any directories that were specified during `configure',
|
|
||||||
but not in terms of `${prefix}', must each be overridden at install
|
|
||||||
time for the entire installation to be relocated. The approach of
|
|
||||||
makefile variable overrides for each directory variable is required by
|
|
||||||
the GNU Coding Standards, and ideally causes no recompilation.
|
|
||||||
However, some platforms have known limitations with the semantics of
|
|
||||||
shared libraries that end up requiring recompilation when using this
|
|
||||||
method, particularly noticeable in packages that use GNU Libtool.
|
|
||||||
|
|
||||||
The second method involves providing the `DESTDIR' variable. For
|
|
||||||
example, `make install DESTDIR=/alternate/directory' will prepend
|
|
||||||
`/alternate/directory' before all installation names. The approach of
|
|
||||||
`DESTDIR' overrides is not required by the GNU Coding Standards, and
|
|
||||||
does not work on platforms that have drive letters. On the other hand,
|
|
||||||
it does better at avoiding recompilation issues, and works well even
|
|
||||||
when some directory options were not specified in terms of `${prefix}'
|
|
||||||
at `configure' time.
|
|
||||||
|
|
||||||
Optional Features
|
|
||||||
=================
|
|
||||||
|
|
||||||
If the package supports it, you can cause programs to be installed
|
|
||||||
with an extra prefix or suffix on their names by giving `configure' the
|
|
||||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
|
||||||
|
|
||||||
Some packages pay attention to `--enable-FEATURE' options to
|
|
||||||
`configure', where FEATURE indicates an optional part of the package.
|
|
||||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
|
||||||
is something like `gnu-as' or `x' (for the X Window System). The
|
|
||||||
`README' should mention any `--enable-' and `--with-' options that the
|
|
||||||
package recognizes.
|
|
||||||
|
|
||||||
For packages that use the X Window System, `configure' can usually
|
|
||||||
find the X include and library files automatically, but if it doesn't,
|
|
||||||
you can use the `configure' options `--x-includes=DIR' and
|
|
||||||
`--x-libraries=DIR' to specify their locations.
|
|
||||||
|
|
||||||
Some packages offer the ability to configure how verbose the
|
|
||||||
execution of `make' will be. For these packages, running `./configure
|
|
||||||
--enable-silent-rules' sets the default to minimal output, which can be
|
|
||||||
overridden with `make V=1'; while running `./configure
|
|
||||||
--disable-silent-rules' sets the default to verbose, which can be
|
|
||||||
overridden with `make V=0'.
|
|
||||||
|
|
||||||
Particular systems
|
|
||||||
==================
|
|
||||||
|
|
||||||
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
|
|
||||||
CC is not installed, it is recommended to use the following options in
|
|
||||||
order to use an ANSI C compiler:
|
|
||||||
|
|
||||||
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
|
|
||||||
|
|
||||||
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
|
|
||||||
|
|
||||||
HP-UX `make' updates targets which have the same time stamps as
|
|
||||||
their prerequisites, which makes it generally unusable when shipped
|
|
||||||
generated files such as `configure' are involved. Use GNU `make'
|
|
||||||
instead.
|
|
||||||
|
|
||||||
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
|
|
||||||
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
|
|
||||||
a workaround. If GNU CC is not installed, it is therefore recommended
|
|
||||||
to try
|
|
||||||
|
|
||||||
./configure CC="cc"
|
|
||||||
|
|
||||||
and if that doesn't work, try
|
|
||||||
|
|
||||||
./configure CC="cc -nodtk"
|
|
||||||
|
|
||||||
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
|
|
||||||
directory contains several dysfunctional programs; working variants of
|
|
||||||
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
|
|
||||||
in your `PATH', put it _after_ `/usr/bin'.
|
|
||||||
|
|
||||||
On Haiku, software installed for all users goes in `/boot/common',
|
|
||||||
not `/usr/local'. It is recommended to use the following options:
|
|
||||||
|
|
||||||
./configure --prefix=/boot/common
|
|
||||||
|
|
||||||
Specifying the System Type
|
|
||||||
==========================
|
|
||||||
|
|
||||||
There may be some features `configure' cannot figure out
|
|
||||||
automatically, but needs to determine by the type of machine the package
|
|
||||||
will run on. Usually, assuming the package is built to be run on the
|
|
||||||
_same_ architectures, `configure' can figure that out, but if it prints
|
|
||||||
a message saying it cannot guess the machine type, give it the
|
|
||||||
`--build=TYPE' option. TYPE can either be a short name for the system
|
|
||||||
type, such as `sun4', or a canonical name which has the form:
|
|
||||||
|
|
||||||
CPU-COMPANY-SYSTEM
|
|
||||||
|
|
||||||
where SYSTEM can have one of these forms:
|
|
||||||
|
|
||||||
OS
|
|
||||||
KERNEL-OS
|
|
||||||
|
|
||||||
See the file `config.sub' for the possible values of each field. If
|
|
||||||
`config.sub' isn't included in this package, then this package doesn't
|
|
||||||
need to know the machine type.
|
|
||||||
|
|
||||||
If you are _building_ compiler tools for cross-compiling, you should
|
|
||||||
use the option `--target=TYPE' to select the type of system they will
|
|
||||||
produce code for.
|
|
||||||
|
|
||||||
If you want to _use_ a cross compiler, that generates code for a
|
|
||||||
platform different from the build platform, you should specify the
|
|
||||||
"host" platform (i.e., that on which the generated programs will
|
|
||||||
eventually be run) with `--host=TYPE'.
|
|
||||||
|
|
||||||
Sharing Defaults
|
|
||||||
================
|
|
||||||
|
|
||||||
If you want to set default values for `configure' scripts to share,
|
|
||||||
you can create a site shell script called `config.site' that gives
|
|
||||||
default values for variables like `CC', `cache_file', and `prefix'.
|
|
||||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
|
||||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
|
||||||
`CONFIG_SITE' environment variable to the location of the site script.
|
|
||||||
A warning: not all `configure' scripts look for a site script.
|
|
||||||
|
|
||||||
Defining Variables
|
|
||||||
==================
|
|
||||||
|
|
||||||
Variables not defined in a site shell script can be set in the
|
|
||||||
environment passed to `configure'. However, some packages may run
|
|
||||||
configure again during the build, and the customized values of these
|
|
||||||
variables may be lost. In order to avoid this problem, you should set
|
|
||||||
them in the `configure' command line, using `VAR=value'. For example:
|
|
||||||
|
|
||||||
./configure CC=/usr/local2/bin/gcc
|
|
||||||
|
|
||||||
causes the specified `gcc' to be used as the C compiler (unless it is
|
|
||||||
overridden in the site shell script).
|
|
||||||
|
|
||||||
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
|
||||||
an Autoconf limitation. Until the limitation is lifted, you can use
|
|
||||||
this workaround:
|
|
||||||
|
|
||||||
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
|
|
||||||
|
|
||||||
`configure' Invocation
|
|
||||||
======================
|
|
||||||
|
|
||||||
`configure' recognizes the following options to control how it
|
|
||||||
operates.
|
|
||||||
|
|
||||||
`--help'
|
|
||||||
`-h'
|
|
||||||
Print a summary of all of the options to `configure', and exit.
|
|
||||||
|
|
||||||
`--help=short'
|
|
||||||
`--help=recursive'
|
|
||||||
Print a summary of the options unique to this package's
|
|
||||||
`configure', and exit. The `short' variant lists options used
|
|
||||||
only in the top level, while the `recursive' variant lists options
|
|
||||||
also present in any nested packages.
|
|
||||||
|
|
||||||
`--version'
|
|
||||||
`-V'
|
|
||||||
Print the version of Autoconf used to generate the `configure'
|
|
||||||
script, and exit.
|
|
||||||
|
|
||||||
`--cache-file=FILE'
|
|
||||||
Enable the cache: use and save the results of the tests in FILE,
|
|
||||||
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
|
||||||
disable caching.
|
|
||||||
|
|
||||||
`--config-cache'
|
|
||||||
`-C'
|
|
||||||
Alias for `--cache-file=config.cache'.
|
|
||||||
|
|
||||||
`--quiet'
|
|
||||||
`--silent'
|
|
||||||
`-q'
|
|
||||||
Do not print messages saying which checks are being made. To
|
|
||||||
suppress all normal output, redirect it to `/dev/null' (any error
|
|
||||||
messages will still be shown).
|
|
||||||
|
|
||||||
`--srcdir=DIR'
|
|
||||||
Look for the package's source code in directory DIR. Usually
|
|
||||||
`configure' can determine that directory automatically.
|
|
||||||
|
|
||||||
`--prefix=DIR'
|
|
||||||
Use DIR as the installation prefix. *note Installation Names::
|
|
||||||
for more details, including other options available for fine-tuning
|
|
||||||
the installation locations.
|
|
||||||
|
|
||||||
`--no-create'
|
|
||||||
`-n'
|
|
||||||
Run the configure checks, but stop before creating any output
|
|
||||||
files.
|
|
||||||
|
|
||||||
`configure' also accepts some other, not widely useful, options. Run
|
|
||||||
`configure --help' for more details.
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2014-2016 Martin Lund <martin.lund@keep-it-simple.com>
|
Copyright (c) 2014-2025 Martin Lund <martin.lund@keep-it-simple.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
This program is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU General Public License
|
modify it under the terms of the GNU General Public License
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
SUBDIRS = src man
|
|
||||||
103
README
103
README
|
|
@ -1,103 +0,0 @@
|
||||||
################################################################################
|
|
||||||
################# tio - a simple TTY terminal I/O application ##################
|
|
||||||
################################################################################
|
|
||||||
|
|
||||||
|
|
||||||
1. Introduction
|
|
||||||
|
|
||||||
"tio" is a simple TTY terminal application which features a straightforward
|
|
||||||
commandline interface to easily connect to TTY devices for basic
|
|
||||||
input/output.
|
|
||||||
|
|
||||||
It was created because the author needed a simple no-nonsense TTY
|
|
||||||
terminal application to easily connect to various terminal TTY devices.
|
|
||||||
|
|
||||||
|
|
||||||
2. Usage
|
|
||||||
|
|
||||||
The commandline interface is straightforward as reflected in the output
|
|
||||||
from 'tio --help':
|
|
||||||
|
|
||||||
|
|
||||||
Usage: tio [<options>] <tty device>
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-b, --baudrate <bps> Baud rate (default: 115200)
|
|
||||||
-d, --databits 5|6|7|8 Data bits (default: 8)
|
|
||||||
-f, --flow hard|soft|none Flow control (default: none)
|
|
||||||
-s, --stopbits 1|2 Stop bits (default: 1)
|
|
||||||
-p, --parity even|odd|none Parity (default: none)
|
|
||||||
-o, --output-delay <ms> Output delay (default: 0)
|
|
||||||
-n, --no-autoconnect Disable automatic connect
|
|
||||||
-l, --log <filename> Log to file
|
|
||||||
-v, --version Display version
|
|
||||||
-h, --help Display help
|
|
||||||
|
|
||||||
In session, press ctrl-t q to quit.
|
|
||||||
|
|
||||||
|
|
||||||
The only option which requires a bit of elaboration is perhaps the
|
|
||||||
--no-autoconnect option.
|
|
||||||
|
|
||||||
By default tio automatically connects to the provided device if present.
|
|
||||||
If the device is not present, it will wait for it to appear and then
|
|
||||||
connect. If the connection is lost (eg. device disconnects), it will wait
|
|
||||||
for the device to reappear and then reconnect.
|
|
||||||
|
|
||||||
However, if the --no-autoconnect option is provided, tio will exit if the
|
|
||||||
device is not present or an established connection is lost.
|
|
||||||
|
|
||||||
Tio features full bash autocompletion support.
|
|
||||||
|
|
||||||
Tio also supports various key commands. Press ctrl-t ? to list the
|
|
||||||
available key commands.
|
|
||||||
|
|
||||||
See the tio man page for more details.
|
|
||||||
|
|
||||||
|
|
||||||
3. Download
|
|
||||||
|
|
||||||
The latest release tarball is available at https://tio.github.io
|
|
||||||
|
|
||||||
|
|
||||||
4. Installation
|
|
||||||
|
|
||||||
Installation steps (from source):
|
|
||||||
|
|
||||||
$ ./configure
|
|
||||||
$ make
|
|
||||||
$ make install
|
|
||||||
|
|
||||||
See INSTALL file for more installation details.
|
|
||||||
|
|
||||||
|
|
||||||
Installation steps (binary package):
|
|
||||||
|
|
||||||
Tio comes prepackaged for various GNU/Linux distributions. Visit
|
|
||||||
https://tio.github.io to see package installation details.
|
|
||||||
|
|
||||||
|
|
||||||
5. Contributing
|
|
||||||
|
|
||||||
Tio is open source. Any contributions (bug fixes, doc, ideas, etc.) are
|
|
||||||
welcome. Visit the tio GitHub page to access latest source code, create
|
|
||||||
pull requests, add issues etc..
|
|
||||||
|
|
||||||
GitHub: https://github.com/tio/tio
|
|
||||||
|
|
||||||
|
|
||||||
6. Support
|
|
||||||
|
|
||||||
Submit bug reports via GitHub: https://github.com/tio/tio/issues
|
|
||||||
|
|
||||||
|
|
||||||
7. License
|
|
||||||
|
|
||||||
Tio is GPLv2+. See COPYING file for license details.
|
|
||||||
|
|
||||||
|
|
||||||
8. Authors
|
|
||||||
|
|
||||||
Created by Martin Lund <martin.lund@keep-it-simple.com>
|
|
||||||
|
|
||||||
See the AUTHORS file for full list of authors.
|
|
||||||
574
README.md
Normal file
574
README.md
Normal file
|
|
@ -0,0 +1,574 @@
|
||||||
|
|
||||||
|
[]()
|
||||||
|
|
||||||
|
# tio - a serial device I/O tool
|
||||||
|
|
||||||
|
[](https://github.com/tio/tio/actions/workflows/ubuntu.yml)
|
||||||
|
[](https://github.com/tio/tio/actions/workflows/macos.yml)
|
||||||
|
[](https://github.com/tio/tio/actions/workflows/codeql.yml)
|
||||||
|
[](https://www.codefactor.io/repository/github/tio/tio)
|
||||||
|
[](https://github.com/tio/tio/releases)
|
||||||
|
[](https://repology.org/project/tio/versions)
|
||||||
|
|
||||||
|
## 1. Introduction
|
||||||
|
|
||||||
|
tio is a serial device tool which features a straightforward command-line and
|
||||||
|
configuration file interface to easily connect to serial TTY devices for basic
|
||||||
|
I/O operations.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="images/tio-demo.gif">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
### 1.1 Motivation
|
||||||
|
|
||||||
|
To make a simpler serial device tool for working with serial TTY devices with
|
||||||
|
less focus on classic terminal/modem features and more focus on the needs of
|
||||||
|
embedded developers and hackers.
|
||||||
|
|
||||||
|
tio was originally created as an alternative to
|
||||||
|
[screen](https://www.gnu.org/software/screen) for connecting to serial devices
|
||||||
|
when used in combination with [tmux](https://tmux.github.io).
|
||||||
|
|
||||||
|
## 2. Features
|
||||||
|
|
||||||
|
* Easily connect to serial TTY devices
|
||||||
|
* Sensible defaults (115200 8n1)
|
||||||
|
* Automatic connection management
|
||||||
|
* Automatic detection of serial ports
|
||||||
|
* Automatic reconnect
|
||||||
|
* Automatically connect to first new appearing serial device
|
||||||
|
* Automatically connect to latest registered serial device
|
||||||
|
* Connect to same port/device combination via unique topology ID (TID)
|
||||||
|
* Useful for reconnecting when serial device has no serial device by ID
|
||||||
|
* Support for non-standard baud rates
|
||||||
|
* Support for mark and space parity
|
||||||
|
* X-modem (1K/CRC) and Y-modem file upload
|
||||||
|
* Support for RS-485 mode
|
||||||
|
* List available serial devices
|
||||||
|
* By device
|
||||||
|
* Including topology ID, uptime, driver, description
|
||||||
|
* Sorted by uptime (newest device listed last)
|
||||||
|
* By ID
|
||||||
|
* By path
|
||||||
|
* Show RX/TX statistics
|
||||||
|
* Toggle serial lines
|
||||||
|
* Pulse serial lines with configurable pulse duration
|
||||||
|
* Local echo support
|
||||||
|
* Remapping of characters (nl, cr-nl, bs, lowercase to uppercase, etc.)
|
||||||
|
* Switchable independent input and output
|
||||||
|
* Normal mode
|
||||||
|
* Hex mode (output supports variable width)
|
||||||
|
* Line mode (input only)
|
||||||
|
* Timestamp support
|
||||||
|
* Per line in normal output mode
|
||||||
|
* Output timeout timestamps in hex output mode
|
||||||
|
* Support for delayed output
|
||||||
|
* Per character
|
||||||
|
* Per line
|
||||||
|
* Log to file
|
||||||
|
* Automatic naming of log file (default)
|
||||||
|
* Configurable directory for saving automatic named log files
|
||||||
|
* Manual naming of log file
|
||||||
|
* Overwrite (default) or append to log file
|
||||||
|
* Strip control characters and escape sequences
|
||||||
|
* Configuration file support
|
||||||
|
* Support for configuration profiles
|
||||||
|
* Activate configuration profiles by name or pattern
|
||||||
|
* Support for including other configuration files
|
||||||
|
* Redirect I/O of shell command to serial device
|
||||||
|
* Redirect I/O to UNIX socket or IPv4/v6 network socket
|
||||||
|
* Useful for scripting or TTY sharing
|
||||||
|
* Pipe input and/or output
|
||||||
|
* Bash completion on options, serial device names, and profile names
|
||||||
|
* Configurable tio message text color
|
||||||
|
* Supports NO_COLOR env variable as per [no-color.org](https://no-color.org)
|
||||||
|
* Visual or audible alert on connect/disconnect
|
||||||
|
* Remapping of prefix key
|
||||||
|
* Lua scripting support for automation
|
||||||
|
* Run script manually or automatically at connect (once/always/never)
|
||||||
|
* Simple expect/send like functionality with support for regular expressions
|
||||||
|
* Manipulate port modem lines (useful for microcontroller reset/boot etc.)
|
||||||
|
* Send files via x/y-modem protocol
|
||||||
|
* Search for serial devices
|
||||||
|
* Man page documentation
|
||||||
|
* Plays nicely with [tmux](https://tmux.github.io) and similar terminal multiplexers
|
||||||
|
|
||||||
|
## 3. Usage
|
||||||
|
|
||||||
|
For more usage details please see the man page documentation
|
||||||
|
[here](https://raw.githubusercontent.com/tio/tio/master/man/tio.1.txt).
|
||||||
|
|
||||||
|
### 3.1 Command-line
|
||||||
|
|
||||||
|
The command-line interface is straightforward as reflected in the output from
|
||||||
|
```tio --help```:
|
||||||
|
```
|
||||||
|
Usage: tio [<options>] <tty-device|profile|tid>
|
||||||
|
|
||||||
|
Connect to TTY device directly or via configuration profile or topology ID.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-b, --baudrate <bps> Baud rate (default: 115200)
|
||||||
|
-d, --databits 5|6|7|8 Data bits (default: 8)
|
||||||
|
-f, --flow hard|soft|none Flow control (default: none)
|
||||||
|
-s, --stopbits 1|2 Stop bits (default: 1)
|
||||||
|
-p, --parity odd|even|none|mark|space Parity (default: none)
|
||||||
|
-o, --output-delay <ms> Output character delay (default: 0)
|
||||||
|
-O, --output-line-delay <ms> Output line delay (default: 0)
|
||||||
|
--line-pulse-duration <duration> Set line pulse duration
|
||||||
|
-a, --auto-connect new|latest|direct Automatic connect strategy (default: direct)
|
||||||
|
--exclude-devices <pattern> Exclude devices by pattern
|
||||||
|
--exclude-drivers <pattern> Exclude drivers by pattern
|
||||||
|
--exclude-tids <pattern> Exclude topology IDs by pattern
|
||||||
|
-n, --no-reconnect Do not reconnect
|
||||||
|
-e, --local-echo Enable local echo
|
||||||
|
--input-mode normal|hex|line Select input mode (default: normal)
|
||||||
|
--output-mode normal|hex|hexN Select output mode (default: normal)
|
||||||
|
-t, --timestamp Enable line timestamp
|
||||||
|
--timestamp-format <format> Set timestamp format (default: 24hour)
|
||||||
|
--timestamp-timeout <ms> Set timestamp timeout (default: 200)
|
||||||
|
-l, --list List available serial devices, TIDs, and profiles
|
||||||
|
-L, --log Enable log to file
|
||||||
|
--log-file <filename> Set log filename
|
||||||
|
--log-directory <path> Set log directory path for automatic named logs
|
||||||
|
--log-append Append to log file
|
||||||
|
--log-strip Strip control characters and escape sequences
|
||||||
|
-m, --map <flags> Map characters
|
||||||
|
-c, --color 0..255|bold|none|list Colorize tio text (default: bold)
|
||||||
|
-S, --socket <socket> Redirect I/O to socket
|
||||||
|
--rs-485 Enable RS-485 mode
|
||||||
|
--rs-485-config <config> Set RS-485 configuration
|
||||||
|
--alert bell|blink|none Alert on connect/disconnect (default: none)
|
||||||
|
--mute Mute tio messages
|
||||||
|
--script <string> Run script from string
|
||||||
|
--script-file <filename> Run script from file
|
||||||
|
--script-run once|always|never Run script on connect (default: always)
|
||||||
|
--exec <command> Execute shell command with I/O redirected to device
|
||||||
|
-v, --version Display version
|
||||||
|
-h, --help Display help
|
||||||
|
|
||||||
|
Options and profiles may be set via configuration file.
|
||||||
|
|
||||||
|
In session you can press ctrl-t ? to list available key commands.
|
||||||
|
|
||||||
|
See the man page for more details.
|
||||||
|
```
|
||||||
|
|
||||||
|
By default tio automatically connects to the provided TTY device. If the device
|
||||||
|
is not present, tio will wait for it to appear and then connect. If the
|
||||||
|
connection is lost (e.g. device is unplugged), it will wait for the device to
|
||||||
|
reappear and then reconnect. However, if the `--no-reconnect` option is
|
||||||
|
provided, tio will exit if the device is not present or an established
|
||||||
|
connection is lost.
|
||||||
|
|
||||||
|
#### 3.1.1 Examples
|
||||||
|
|
||||||
|
Typical use is without options:
|
||||||
|
```
|
||||||
|
$ tio /dev/ttyUSB0
|
||||||
|
```
|
||||||
|
|
||||||
|
Which corresponds to the commonly used default options:
|
||||||
|
```
|
||||||
|
$ tio --baudrate 115200 --databits 8 --flow none --stopbits 1 --parity none /dev/ttyUSB0
|
||||||
|
```
|
||||||
|
|
||||||
|
List available serial devices:
|
||||||
|
```
|
||||||
|
$ tio --list
|
||||||
|
Device TID Uptime [s] Driver Description
|
||||||
|
----------------- ---- ------------- ---------------- --------------------------
|
||||||
|
/dev/ttyS4 BaaB 19526.576 port 16550A UART
|
||||||
|
/dev/ttyS5 eV0Z 19525.845 port 16550A UART
|
||||||
|
/dev/ttyUSB1 bCC2 1023.274 ftdi_sio TTL232R-3V3
|
||||||
|
/dev/ttyUSB0 SPpw 978.527 ftdi_sio TTL232RG-VREG3V3
|
||||||
|
/dev/ttyACM0 i5q4 2.079 cdc_acm ST-Link VCP Ctrl
|
||||||
|
|
||||||
|
By-id
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
/dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTCHUV56-if00-port0
|
||||||
|
/dev/serial/by-id/usb-FTDI_TTL232RG-VREG3V3_FT1NELUB-if00-port0
|
||||||
|
/dev/serial/by-id/usb-STMicroelectronics_STLINK-V3_004900343438510234313939-if02
|
||||||
|
|
||||||
|
By-path
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
/dev/serial/by-path/pci-0000:00:14.0-usb-0:8.1.3.1.4:1.0-port0
|
||||||
|
/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:8.1.3.1.4:1.0-port0
|
||||||
|
/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.4:1.0-port0
|
||||||
|
/dev/serial/by-path/pci-0000:00:14.0-usb-0:6.4:1.0-port0
|
||||||
|
/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.3:1.2
|
||||||
|
/dev/serial/by-path/pci-0000:00:14.0-usb-0:6.3:1.2
|
||||||
|
|
||||||
|
Configuration profiles (/home/lundmar/.config/tio/config)
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
rpi3 stm32 esp32 am64-evm
|
||||||
|
imx8mp-evk nucleo-h743zi2 usb-devices
|
||||||
|
```
|
||||||
|
|
||||||
|
It is recommended to connect serial TTY devices by ID:
|
||||||
|
```
|
||||||
|
$ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTCHUV56-if00-port0
|
||||||
|
```
|
||||||
|
Note: Using serial devices by ID helps ensure that tio automatically reconnects
|
||||||
|
to the same serial device when reattached, even when it enumerates differently.
|
||||||
|
|
||||||
|
If no serial device by ID is available it is recommended to connect via
|
||||||
|
topology ID (TID):
|
||||||
|
```
|
||||||
|
$ tio bCC2
|
||||||
|
```
|
||||||
|
Note: The TID is unique and will stay the same as long as your USB serial port
|
||||||
|
device plugs into the same USB topology (same ports, same hubs, same
|
||||||
|
connections, etc.). This way it is possible for tio to successfully reconnect
|
||||||
|
to the same device.
|
||||||
|
|
||||||
|
Connect automatically to first new appearing serial device:
|
||||||
|
```
|
||||||
|
$ tio --auto-connect new
|
||||||
|
```
|
||||||
|
|
||||||
|
Connect automatically to latest registered serial device:
|
||||||
|
```
|
||||||
|
$ tio --auto-connect latest
|
||||||
|
```
|
||||||
|
|
||||||
|
It is possible to use exclude options to affect which serial devices are
|
||||||
|
involved in the automatic connection strategy:
|
||||||
|
```
|
||||||
|
$ tio --auto-connect new --exclude-devices "/dev/ttyACM?,/dev/ttyUSB2"
|
||||||
|
```
|
||||||
|
|
||||||
|
And to exclude drivers by pattern:
|
||||||
|
```
|
||||||
|
$ tio --auto-connect new --exclude-drivers "cdc_acm,ftdi_sio"
|
||||||
|
```
|
||||||
|
Note: Pattern matching supports '*' and '?'. Use comma separation to define
|
||||||
|
multiple patterns.
|
||||||
|
|
||||||
|
To include drivers by specific pattern simply negate the exclude option:
|
||||||
|
```
|
||||||
|
$ tio --auto-connect new --exclude-drivers !("cp2102")
|
||||||
|
```
|
||||||
|
|
||||||
|
Log to file with autogenerated filename:
|
||||||
|
```
|
||||||
|
$ tio --log /dev/ttyUSB0
|
||||||
|
```
|
||||||
|
|
||||||
|
Log to file with specific filename:
|
||||||
|
```
|
||||||
|
$ tio --log --log-file my-log.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable ISO8601 timestamps per line:
|
||||||
|
```
|
||||||
|
$ tio --timestamp --timestamp-format iso8601 /dev/ttyUSB0
|
||||||
|
```
|
||||||
|
|
||||||
|
Output to hex with width 16:
|
||||||
|
```
|
||||||
|
$ tio --output-mode hex16 /dev/ttyUSB0
|
||||||
|
```
|
||||||
|
|
||||||
|
Redirect I/O to IPv4 network socket on port 4242:
|
||||||
|
```
|
||||||
|
$ tio --socket inet:4242 /dev/ttyUSB0
|
||||||
|
```
|
||||||
|
|
||||||
|
Map NL to CR-NL on input from device and DEL to BS on output to device:
|
||||||
|
```
|
||||||
|
$ tio --map INLCRNL,ODELBS /dev/ttyUSB0
|
||||||
|
```
|
||||||
|
|
||||||
|
Pipe data to the serial device:
|
||||||
|
```
|
||||||
|
$ cat data.bin | tio /dev/ttyUSB0
|
||||||
|
```
|
||||||
|
|
||||||
|
Manipulate modem lines on connect:
|
||||||
|
```
|
||||||
|
$ tio --script "tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{DTR=toggle,RTS=toggle}" /dev/ttyUSB0
|
||||||
|
```
|
||||||
|
|
||||||
|
Pipe command to serial device and wait for line response within 1 second:
|
||||||
|
```
|
||||||
|
$ echo "*IDN?" | tio /dev/ttyACM0 --script "tio.expect('\r\n', 1000)" --mute
|
||||||
|
KORAD KD3305P V4.2 SN:32475045
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 Key commands
|
||||||
|
|
||||||
|
Various in session key commands are supported. When tio is started, press
|
||||||
|
ctrl-t ? to list the available key commands.
|
||||||
|
|
||||||
|
```
|
||||||
|
[15:02:53.269] Key commands:
|
||||||
|
[15:02:53.269] ctrl-t ? List available key commands
|
||||||
|
[15:02:53.269] ctrl-t b Send break
|
||||||
|
[15:02:53.269] ctrl-t c Show configuration
|
||||||
|
[15:02:53.269] ctrl-t e Toggle local echo mode
|
||||||
|
[15:02:53.269] ctrl-t f Toggle log to file
|
||||||
|
[15:02:53.269] ctrl-t F Flush data I/O buffers
|
||||||
|
[15:02:53.269] ctrl-t g Toggle serial port line
|
||||||
|
[15:02:53.269] ctrl-t i Toggle input mode
|
||||||
|
[15:02:53.269] ctrl-t l Clear screen
|
||||||
|
[15:02:53.269] ctrl-t L Show line states
|
||||||
|
[15:02:53.269] ctrl-t m Change mapping of characters on input or output
|
||||||
|
[15:02:53.269] ctrl-t o Toggle output mode
|
||||||
|
[15:02:53.269] ctrl-t p Pulse serial port line
|
||||||
|
[15:02:53.269] ctrl-t q Quit
|
||||||
|
[15:02:53.269] ctrl-t r Run script
|
||||||
|
[15:02:53.269] ctrl-t R Execute shell command with I/O redirected to device
|
||||||
|
[15:02:53.269] ctrl-t s Show statistics
|
||||||
|
[15:02:53.269] ctrl-t t Toggle line timestamp mode
|
||||||
|
[15:02:53.269] ctrl-t v Show version
|
||||||
|
[15:02:53.269] ctrl-t x Send file via Xmodem
|
||||||
|
[15:02:53.269] ctrl-t y Send file via Ymodem
|
||||||
|
[15:02:53.269] ctrl-t ctrl-t Send ctrl-t character
|
||||||
|
```
|
||||||
|
|
||||||
|
If needed, the prefix key (ctrl-t) can be remapped via configuration file.
|
||||||
|
|
||||||
|
### 3.3 Configuration file
|
||||||
|
|
||||||
|
Options can be set via the configuration file first found in any of the
|
||||||
|
following locations in the order listed:
|
||||||
|
- $XDG_CONFIG_HOME/tio/config
|
||||||
|
- $HOME/.config/tio/config
|
||||||
|
- $HOME/.tioconfig
|
||||||
|
|
||||||
|
The configuration file supports profiles using named sections which can be
|
||||||
|
activated via the command-line by name or pattern. A profile specifies which
|
||||||
|
TTY device to connect to and other options.
|
||||||
|
|
||||||
|
### 3.3.1 Example
|
||||||
|
|
||||||
|
Example configuration file:
|
||||||
|
|
||||||
|
```
|
||||||
|
[default]
|
||||||
|
baudrate = 115200
|
||||||
|
databits = 8
|
||||||
|
parity = none
|
||||||
|
stopbits = 1
|
||||||
|
color = 10
|
||||||
|
|
||||||
|
[rpi3]
|
||||||
|
device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
|
||||||
|
no-reconnect = true
|
||||||
|
log = true
|
||||||
|
log-file = rpi3.log
|
||||||
|
line-pulse-duration = DTR=200,RTS=150
|
||||||
|
color = 11
|
||||||
|
|
||||||
|
[svf2]
|
||||||
|
device = /dev/ttyUSB0
|
||||||
|
baudrate = 9600
|
||||||
|
script = tio.expect("login: "); tio.write("root\n"); tio.expect("Password: "); tio.write("root\n")
|
||||||
|
color = 12
|
||||||
|
|
||||||
|
[esp32]
|
||||||
|
device = /dev/serial/by-id/usb-0403_6014-if00-port0
|
||||||
|
script = tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{DTR=low,RTS=high}; tio.msleep(100); tio.set{RTS=low}
|
||||||
|
script-run = once
|
||||||
|
color = 13
|
||||||
|
|
||||||
|
[usb-devices]
|
||||||
|
pattern = ^usb([0-9]*)
|
||||||
|
device = /dev/ttyUSB%m1
|
||||||
|
color = 14
|
||||||
|
```
|
||||||
|
|
||||||
|
To use a specific profile by name simply start tio like so:
|
||||||
|
```
|
||||||
|
$ tio rpi3
|
||||||
|
```
|
||||||
|
Or by pattern match:
|
||||||
|
```
|
||||||
|
$ tio usb12
|
||||||
|
```
|
||||||
|
|
||||||
|
Another more elaborate configuration file example is available [here](examples/config/config).
|
||||||
|
|
||||||
|
### 3.4 Lua script API
|
||||||
|
|
||||||
|
Tio suppots Lua scripting to easily automate interaction with the tty device.
|
||||||
|
|
||||||
|
In addition to the standard Lua API tio makes the following functions
|
||||||
|
and variables available:
|
||||||
|
|
||||||
|
|
||||||
|
#### `tio.expect(pattern, timeout)`
|
||||||
|
|
||||||
|
Waits for the Lua pattern to match or timeout before continuing.
|
||||||
|
Timeout is in milliseconds, defaults to 0 meaning it will wait forever.
|
||||||
|
|
||||||
|
Returns the captures from the pattern or `nil` on timeout.
|
||||||
|
|
||||||
|
#### `tio.read(size, timeout)`
|
||||||
|
|
||||||
|
Read up to `size` bytes from serial device. If timeout is 0 or not provided it
|
||||||
|
will wait forever until data is ready to read.
|
||||||
|
|
||||||
|
Returns a string up to `size` bytes long on success and `nil` on timeout.
|
||||||
|
|
||||||
|
#### `tio.readline(timeout)`
|
||||||
|
|
||||||
|
Read line from serial device. If timeout is 0 or not provided it will wait
|
||||||
|
forever until data is ready to read.
|
||||||
|
|
||||||
|
Returns a string on success and `nil` on timeout. On timeout a partially read
|
||||||
|
line may be returned as a second return value.
|
||||||
|
|
||||||
|
#### `tio.write(string)`
|
||||||
|
|
||||||
|
Write string to serial device.
|
||||||
|
|
||||||
|
Returns the `tio` table.
|
||||||
|
|
||||||
|
#### `tio.send(file, protocol)`
|
||||||
|
|
||||||
|
Send file using x/y-modem protocol.
|
||||||
|
|
||||||
|
Protocol can be any of `XMODEM_1K`, `XMODEM_CRC`, `YMODEM`.
|
||||||
|
|
||||||
|
#### `tio.ttysearch()`
|
||||||
|
|
||||||
|
Search for serial devices.
|
||||||
|
|
||||||
|
Returns a table of number indexed tables, one for each serial device found.
|
||||||
|
Each of these tables contains the serial device information accessible via the
|
||||||
|
following string indexed elements "path", "tid", "uptime", "driver",
|
||||||
|
"description".
|
||||||
|
|
||||||
|
Returns `nil` if no serial devices are found.
|
||||||
|
|
||||||
|
#### `tio.set{line=state, ...}`
|
||||||
|
Set state of one or multiple tty modem lines.
|
||||||
|
|
||||||
|
Line can be any of `DTR`, `RTS`, `CTS`, `DSR`, `CD`, `RI`
|
||||||
|
|
||||||
|
State is `high`, `low`, or `toggle`.
|
||||||
|
|
||||||
|
#### `tio.sleep(seconds)`
|
||||||
|
|
||||||
|
Sleep for seconds.
|
||||||
|
|
||||||
|
#### `tio.msleep(ms)`
|
||||||
|
|
||||||
|
Sleep for milliseconds.
|
||||||
|
|
||||||
|
#### `tio.alwaysecho`
|
||||||
|
|
||||||
|
A boolean value, defaults to `true`.
|
||||||
|
|
||||||
|
If `tio.alwaysecho` is `false`, the result of `tio.read`, `tio.readline` or
|
||||||
|
`tio.expect` will only be returned from the function and not logged or printed.
|
||||||
|
|
||||||
|
If `tio.alwaysecho` is set to `true`, reading functions also emit a single
|
||||||
|
timestamp to stdout and log file per `options.timestamp` and `options.log`.
|
||||||
|
|
||||||
|
|
||||||
|
## 4. Installation
|
||||||
|
|
||||||
|
### 4.1 Installation using package manager (Linux)
|
||||||
|
|
||||||
|
Packages for various GNU/Linux distributions are available. Please consult your
|
||||||
|
package manager tool to find and install tio.
|
||||||
|
|
||||||
|
If you would like to see tio included in your favorite distribution, please
|
||||||
|
reach out to its package maintainers team.
|
||||||
|
|
||||||
|
### 4.2 Installation using snap (Linux)
|
||||||
|
|
||||||
|
Install latest stable version:
|
||||||
|
```
|
||||||
|
$ snap install tio --classic
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: Classic confinement is currently required due to limitations of the snapcraft framework.
|
||||||
|
See [Issue #187](https://github.com/tio/tio/issues/187) for discussion.
|
||||||
|
|
||||||
|
### 4.3 Installation using brew (MacOS, Linux)
|
||||||
|
|
||||||
|
If you have [brew](http://brew.sh) installed:
|
||||||
|
```
|
||||||
|
$ brew install tio
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 Installation using MSYS2 (Windows)
|
||||||
|
|
||||||
|
If you have [MSYS2](https://www.msys2.org) installed:
|
||||||
|
```
|
||||||
|
$ pacman -S tio
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.5 Installation from source
|
||||||
|
|
||||||
|
The latest source releases can be found [here](https://github.com/tio/tio/releases).
|
||||||
|
|
||||||
|
Before running the install steps make sure you have glib and lua libraries installed. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo apt install libglib2.0-dev liblua5.2-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Install steps:
|
||||||
|
```
|
||||||
|
$ meson setup build
|
||||||
|
$ meson compile -C build
|
||||||
|
$ meson install -C build
|
||||||
|
```
|
||||||
|
|
||||||
|
See meson\_options.txt for tio specific build options.
|
||||||
|
|
||||||
|
Note: The meson install steps may differ depending on your specific system.
|
||||||
|
|
||||||
|
### 4.6 Known issues
|
||||||
|
|
||||||
|
Getting permission access errors trying to open your serial device?
|
||||||
|
|
||||||
|
Add your user to the group which allows serial device access permanently. For example, to add your user to the 'dialout' group do:
|
||||||
|
```bash
|
||||||
|
sudo usermod -a -G dialout <username>
|
||||||
|
```
|
||||||
|
Switch to the "dialout" group, temporary but immediately for this session.
|
||||||
|
```bash
|
||||||
|
newgrp dialout
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## 5. Contributing
|
||||||
|
|
||||||
|
This is an open source project - all contributions (bug reports, code, doc,
|
||||||
|
ideas, etc.) are welcome.
|
||||||
|
|
||||||
|
Please use the github issue tracker and pull request features.
|
||||||
|
|
||||||
|
Also, if you find this free open source software useful please feel free to
|
||||||
|
consider making a donation of your choice:
|
||||||
|
|
||||||
|
[](https://www.paypal.me/lundmar)
|
||||||
|
|
||||||
|
|
||||||
|
## 6. Support
|
||||||
|
|
||||||
|
Submit bug reports via GitHub: https://github.com/tio/tio/issues
|
||||||
|
|
||||||
|
|
||||||
|
## 7. Website
|
||||||
|
|
||||||
|
Visit [tio.github.io](https://tio.github.io)
|
||||||
|
|
||||||
|
|
||||||
|
## 8. License
|
||||||
|
|
||||||
|
tio is GPLv2+. See LICENSE file for more details.
|
||||||
|
|
||||||
|
|
||||||
|
## 9. Authors
|
||||||
|
|
||||||
|
Maintained by Martin Lund \<martin.lund@keep-it-simple.com>
|
||||||
|
|
||||||
|
See the AUTHORS file for full list of contributors.
|
||||||
111
TODO
Normal file
111
TODO
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
* Add release support for arm and x86 binary tarballs
|
||||||
|
|
||||||
|
* Support input and input mapping from lua scripts
|
||||||
|
|
||||||
|
* Add option to send file raw (no modem protocol)
|
||||||
|
|
||||||
|
* Add loopback option
|
||||||
|
|
||||||
|
Send received serial input back to output (for testing etc.)
|
||||||
|
|
||||||
|
* Add loopback support between two serial ports
|
||||||
|
|
||||||
|
Useful for traffic monitoring
|
||||||
|
|
||||||
|
* Add mapping feature for printing non-printable characters
|
||||||
|
|
||||||
|
* Porting layer to support native win32 builds.
|
||||||
|
|
||||||
|
Some of the work that needs to be done:
|
||||||
|
|
||||||
|
All posix functions need to be platform independent, go though file by file:
|
||||||
|
|
||||||
|
termios.h
|
||||||
|
unistd.h has very limited functions
|
||||||
|
ENV different in config_file_resolve
|
||||||
|
errno
|
||||||
|
sys/ioctl.h
|
||||||
|
sys/poll.h
|
||||||
|
socket, may need a new thread
|
||||||
|
Serial, RS485, character mapping
|
||||||
|
Communication pipe
|
||||||
|
|
||||||
|
Port enumerate, all devices of the same type have the same name (eg. USB
|
||||||
|
Serial Device for ttyACM) -> which makes regex not meaningful (kind of a
|
||||||
|
good thing since libtre in Mingw has too much dependencies makes binary too
|
||||||
|
big)
|
||||||
|
|
||||||
|
* Support traditional hex output format such as:
|
||||||
|
|
||||||
|
00000000 74 65 73 74 20 74 65 73 74 20 74 65 73 74 20 74 |test test test t|
|
||||||
|
00000010 65 73 74 64 66 0a 61 0a 66 61 0a 66 0a 61 73 66 |estdf.a.fa.f.asf|
|
||||||
|
00000020 64 61 64 73 66 61 73 66 64 61 73 64 66 61 64 73 |dadsfasfdasdfads|
|
||||||
|
00000030 66 0a 61 73 64 66 61 64 73 66 61 73 64 66 61 73 |f.asdfadsfasdfas|
|
||||||
|
00000040 64 66 0a 61 73 64 66 61 64 73 66 61 73 64 66 61 |df.asdfadsfasdfa|
|
||||||
|
00000050 73 64 66 66 64 61 73 64 66 0a 0a 31 32 33 31 0a |sdffdasdf..1231.|
|
||||||
|
00000060 65 32 31 64 73 77 65 64 0a 0a |e21dswed..|
|
||||||
|
0000006a
|
||||||
|
|
||||||
|
* Add support for activity based time stamping in normal output mode
|
||||||
|
|
||||||
|
Already supported in hex output mode.
|
||||||
|
|
||||||
|
* Allow tio to connect to socket
|
||||||
|
|
||||||
|
After some more consideration I think it makes sense to support connecting to a
|
||||||
|
socket as that will make tio be able to both serve a serial port via a socket
|
||||||
|
and connect to it - it will be an end to end solution. In short we will be able
|
||||||
|
to do the following:
|
||||||
|
|
||||||
|
Host serial port on socket (existing feature):
|
||||||
|
$ tio --socket unix:/tmp/tio-socket-0 /dev/ttyUSB0
|
||||||
|
|
||||||
|
Connect to same socket (new feature):
|
||||||
|
$ tio unix:/tmp/tio-socket-0
|
||||||
|
|
||||||
|
Besides a bit of refactoring the following required changes spring to mind:
|
||||||
|
|
||||||
|
* Socket mode and type of socket should be activated via device name prefix. For example:
|
||||||
|
* UNIX socket: tio unix:<filename>
|
||||||
|
* TCPv4 socket: tio inet:<ip>:<port>
|
||||||
|
* TCPv6 socket: tio inet6:<ip>:<port>
|
||||||
|
* If no port number defined default to 3333
|
||||||
|
* Mapping flags INLCR, IGNCR, ICRNL needs implementation for socket mode
|
||||||
|
* Error messages should just say "device" instead of "tty device" etc.
|
||||||
|
* Remove other tty'isms (tty_write() should be device_write() etc.)
|
||||||
|
* In session key commands that do not work in socket mode should either not be listed or print an error messages if used.
|
||||||
|
* All non-tty features should continue work (auto-connect etc.)
|
||||||
|
* Shell completion script update
|
||||||
|
* Man page update
|
||||||
|
|
||||||
|
|
||||||
|
* Split I/O feature
|
||||||
|
|
||||||
|
Allow to split input and output so that it is possible to manage these
|
||||||
|
independently.
|
||||||
|
|
||||||
|
The general idea is to redirect the output stream on the socket port number
|
||||||
|
specified but then redirect the input stream on the same port number + 1.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
$ tio /dev/ttyUSB0 --socket inet:4444,split-io
|
||||||
|
|
||||||
|
Will result in output stream being hosted on port 4444 and input stream
|
||||||
|
hosted on port 4445.
|
||||||
|
|
||||||
|
For file sockets something similar can be arranged:
|
||||||
|
|
||||||
|
$ tio /dev/ttyUSB0 --socket unix:/tmp/tio-socket-0,split-io
|
||||||
|
|
||||||
|
Will result in output stream being hosted via /tmp/tio-socket-0 and input
|
||||||
|
stream hosted via /tmp/tio-socket-0_input
|
||||||
|
|
||||||
|
* Websocket support
|
||||||
|
|
||||||
|
Extend the socket feature to redirect serial I/O to websocket on e.g. port
|
||||||
|
1234 like so:
|
||||||
|
|
||||||
|
$ tio --socket ws:1234
|
||||||
|
|
||||||
|
Use libwesockets to implement feature.
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
autoreconf --force -v --install
|
|
||||||
91
configure.ac
91
configure.ac
|
|
@ -1,91 +0,0 @@
|
||||||
AC_PREREQ([2.68])
|
|
||||||
AC_INIT([tio], [1.25], [], [tio], [https://tio.github.io])
|
|
||||||
AC_CONFIG_HEADERS([src/include/config.h])
|
|
||||||
AM_INIT_AUTOMAKE([1.11 foreign dist-xz no-dist-gzip -Wall -Werror])
|
|
||||||
AM_SILENT_RULES([yes])
|
|
||||||
AC_PROG_CC
|
|
||||||
AC_LANG([C])
|
|
||||||
AC_PROG_INSTALL
|
|
||||||
AC_SYS_LARGEFILE
|
|
||||||
|
|
||||||
# Handle bash completion
|
|
||||||
AC_ARG_WITH([bash-completion-dir],
|
|
||||||
AS_HELP_STRING([--with-bash-completion-dir[=PATH]],
|
|
||||||
[Install the bash auto-completion script in this directory. @<:@default=yes@:>@]),
|
|
||||||
[],
|
|
||||||
[with_bash_completion_dir=yes])
|
|
||||||
|
|
||||||
if test "x$with_bash_completion_dir" = "xyes"; then
|
|
||||||
BASH_COMPLETION_DIR="${datadir}/bash-completion/completions"
|
|
||||||
else
|
|
||||||
BASH_COMPLETION_DIR="$with_bash_completion_dir"
|
|
||||||
fi
|
|
||||||
AC_SUBST([BASH_COMPLETION_DIR])
|
|
||||||
AM_CONDITIONAL([ENABLE_BASH_COMPLETION],[test "x$with_bash_completion_dir" != "xno"])
|
|
||||||
|
|
||||||
# TIO_CHECK_BAUDRATE(N)
|
|
||||||
AC_DEFUN(
|
|
||||||
[TIO_CHECK_BAUDRATE],
|
|
||||||
[
|
|
||||||
AC_CHECK_DECL([B$1], [tio_have_decl=1], [tio_have_decl=0], [[#include <termios.h>]])
|
|
||||||
AS_IF([test $tio_have_decl = 1], [
|
|
||||||
AC_SUBST([BAUDRATES], ["$BAUDRATES $1"])
|
|
||||||
AC_SUBST([BAUDRATE_CASES], ["$BAUDRATE_CASES case $1: baudrate = B$1; break;"])]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
# TIO_CHECK_BAUDRATES(N1, N2, ...)
|
|
||||||
AC_DEFUN(
|
|
||||||
[TIO_CHECK_BAUDRATES],
|
|
||||||
[m4_foreach([n], [$@], [TIO_CHECK_BAUDRATE(m4_normalize(n))])]
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check for available terminal I/O speeds
|
|
||||||
BAUDRATES=
|
|
||||||
BAUDRATE_CASES=
|
|
||||||
TIO_CHECK_BAUDRATES(
|
|
||||||
0,
|
|
||||||
50,
|
|
||||||
75,
|
|
||||||
110,
|
|
||||||
134,
|
|
||||||
150,
|
|
||||||
200,
|
|
||||||
300,
|
|
||||||
600,
|
|
||||||
1200,
|
|
||||||
1800,
|
|
||||||
2400,
|
|
||||||
4800,
|
|
||||||
7200,
|
|
||||||
9600,
|
|
||||||
14400,
|
|
||||||
19200,
|
|
||||||
28800,
|
|
||||||
38400,
|
|
||||||
57600,
|
|
||||||
76800,
|
|
||||||
115200,
|
|
||||||
230400,
|
|
||||||
460800,
|
|
||||||
500000,
|
|
||||||
576000,
|
|
||||||
921600,
|
|
||||||
1000000,
|
|
||||||
1152000,
|
|
||||||
1500000,
|
|
||||||
2000000,
|
|
||||||
2500000,
|
|
||||||
3000000,
|
|
||||||
3500000,
|
|
||||||
4000000
|
|
||||||
)
|
|
||||||
|
|
||||||
AC_DEFINE_UNQUOTED([AUTOCONF_BAUDRATE_CASES],[$BAUDRATE_CASES],[Switch cases for detected baud rates])
|
|
||||||
|
|
||||||
AC_CONFIG_FILES([Makefile])
|
|
||||||
AC_CONFIG_FILES([src/Makefile])
|
|
||||||
AC_CONFIG_FILES([src/bash-completion/tio])
|
|
||||||
AC_CONFIG_FILES([man/Makefile])
|
|
||||||
AC_OUTPUT
|
|
||||||
78
examples/config/config
Normal file
78
examples/config/config
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
###############################
|
||||||
|
# tio - https://tio.github.io #
|
||||||
|
###############################
|
||||||
|
|
||||||
|
# Example tio configuration file
|
||||||
|
#
|
||||||
|
# Place file in any of the following locations:
|
||||||
|
# $XDG_CONFIG_HOME/tio/config
|
||||||
|
# $HOME/.config/tio/config
|
||||||
|
# $HOME/.tioconfig
|
||||||
|
|
||||||
|
[default]
|
||||||
|
baudrate = 115200
|
||||||
|
databits = 8
|
||||||
|
flow = none
|
||||||
|
stopbits = 1
|
||||||
|
parity = none
|
||||||
|
output-delay = 0
|
||||||
|
output-line-delay = 0
|
||||||
|
auto-connect = direct
|
||||||
|
no-reconnect = false
|
||||||
|
local-echo = false
|
||||||
|
input-mode = normal
|
||||||
|
output-mode = normal
|
||||||
|
timestamp = false
|
||||||
|
log = false
|
||||||
|
log-append = false
|
||||||
|
log-strip = false
|
||||||
|
color = bold
|
||||||
|
rs-485 = false
|
||||||
|
alert = none
|
||||||
|
script-run = always
|
||||||
|
prefix-ctrl-key = t
|
||||||
|
|
||||||
|
# Configuration profiles
|
||||||
|
|
||||||
|
[rpi3]
|
||||||
|
baudrate = 115200
|
||||||
|
device = /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A6009HU3-if00-port0
|
||||||
|
socket = unix:/tmp/tio-socket-0
|
||||||
|
color = 9
|
||||||
|
|
||||||
|
[am64-evm]
|
||||||
|
baudrate = 115200
|
||||||
|
device = /dev/serial/by-id/usb-Silicon_Labs_CP2105_Dual_USB_to_UART_Bridge_Controller_01093176-if01-port0
|
||||||
|
line-pulse-duration = DTR=200,RTS=300,RI=50
|
||||||
|
alert = bell
|
||||||
|
color = 10
|
||||||
|
|
||||||
|
[tincan]
|
||||||
|
baudrate = 9600
|
||||||
|
device = /dev/serial/by-id/usb-TinCanTools_Flyswatter2_FS20000-if00-port0
|
||||||
|
log = true
|
||||||
|
log-file = tincan.log
|
||||||
|
log-strip = true
|
||||||
|
color = 11
|
||||||
|
|
||||||
|
[usb-devices]
|
||||||
|
pattern = ^usb([0-9]*)
|
||||||
|
device = /dev/ttyUSB%m1
|
||||||
|
color = 12
|
||||||
|
|
||||||
|
[rs-485-device]
|
||||||
|
device = /dev/ttyUSB0
|
||||||
|
rs-485 = true
|
||||||
|
rs-485-config = RTS_ON_SEND=1,RTS_AFTER_SEND=1,RTS_DELAY_BEFORE_SEND=60,RTS_DELAY_AFTER_SEND=80,RX_DURING_TX
|
||||||
|
color = 13
|
||||||
|
|
||||||
|
[esp32]
|
||||||
|
device = /dev/ttyUSB0
|
||||||
|
color = 14
|
||||||
|
script = tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{DTR=low,RTS=high}; tio.msleep(100); tio.set{RTS=low}
|
||||||
|
script-run = always
|
||||||
|
|
||||||
|
[buspirate]
|
||||||
|
device = /dev/ttyACM0
|
||||||
|
map = INLCRNL,ODELBS
|
||||||
|
color = 15
|
||||||
28
examples/lua/automatic-linux-login.lua
Normal file
28
examples/lua/automatic-linux-login.lua
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
local logins = {
|
||||||
|
["foo"] = {
|
||||||
|
username = "foouser",
|
||||||
|
password = "foopass",
|
||||||
|
},
|
||||||
|
["bar"] = {
|
||||||
|
username = "baruser",
|
||||||
|
password = "barpass",
|
||||||
|
},
|
||||||
|
["baz"] = {
|
||||||
|
username = "bazuser",
|
||||||
|
password = "bazpass",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local hostname = tio.expect("^(%g+) login:", 10)
|
||||||
|
if hostname then
|
||||||
|
local login = logins[hostname]
|
||||||
|
if (nil ~= login) then
|
||||||
|
tio.write(login.username .. "\n")
|
||||||
|
tio.expect("Password:")
|
||||||
|
tio.write(login.password .. "\n")
|
||||||
|
else
|
||||||
|
io.write("\r\nDon't know login info for " .. hostname .. "\r\n")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
io.write("\r\nDidn't find a login prompt\r\n")
|
||||||
|
end
|
||||||
5
examples/lua/control-lines-test.lua
Normal file
5
examples/lua/control-lines-test.lua
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
tio.set{DTR=high, RTS=low}
|
||||||
|
tio.msleep(100)
|
||||||
|
tio.set{DTR=low, RTS=high}
|
||||||
|
tio.msleep(100)
|
||||||
|
tio.set{RTS=toggle}
|
||||||
14
examples/lua/read.lua
Normal file
14
examples/lua/read.lua
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
tio.read(1000, 6000) -- initial config
|
||||||
|
tio.write("\n")
|
||||||
|
tio.msleep(100)
|
||||||
|
tio.read(650, 60) -- main menu
|
||||||
|
tio.write("S") -- S menu
|
||||||
|
tio.msleep(30)
|
||||||
|
tio.read(650, 60)
|
||||||
|
tio.write("t") -- Parallel Value Table
|
||||||
|
tio.read(650, 60)
|
||||||
|
while true do
|
||||||
|
tio.msleep(1000)
|
||||||
|
tio.write("t")
|
||||||
|
tio.read(650, 50) -- repeat PVT forever
|
||||||
|
end
|
||||||
15
examples/lua/read_line.lua
Normal file
15
examples/lua/read_line.lua
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
tio.read(1000, 8000) -- read initial config
|
||||||
|
tio.write("\n")
|
||||||
|
tio.read(650, 100) -- main menu
|
||||||
|
tio.write("S") -- S menu
|
||||||
|
repeat
|
||||||
|
str = tio.readline(25)
|
||||||
|
until str == nil
|
||||||
|
while true do
|
||||||
|
tio.write("t") -- query PV table
|
||||||
|
tio.msleep(880)
|
||||||
|
repeat
|
||||||
|
str = tio.readline(60)
|
||||||
|
tio.msleep(60)
|
||||||
|
until str == nil
|
||||||
|
end
|
||||||
13
examples/lua/serial-device-search.lua
Normal file
13
examples/lua/serial-device-search.lua
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
io.write("Searching... ")
|
||||||
|
|
||||||
|
local device = tio.ttysearch()
|
||||||
|
|
||||||
|
io.write("done\r\n")
|
||||||
|
|
||||||
|
for i in ipairs(device) do
|
||||||
|
io.write("\r\n" .. device[i]["path"] .. "\r\n")
|
||||||
|
io.write(" tid = " .. device[i]["tid"] .. "\r\n")
|
||||||
|
io.write(" uptime = " .. device[i]["uptime"] .. "\r\n")
|
||||||
|
io.write(" driver = " .. device[i]["driver"] .. "\r\n")
|
||||||
|
io.write(" description = " .. device[i]["description"] .. "\r\n")
|
||||||
|
end
|
||||||
BIN
images/paypal.png
Normal file
BIN
images/paypal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
images/tio-demo.gif
Normal file
BIN
images/tio-demo.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 MiB |
BIN
images/tio-icon.png
Normal file
BIN
images/tio-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
|
|
@ -1 +0,0 @@
|
||||||
dist_man_MANS = tio.1
|
|
||||||
15
man/meson.build
Normal file
15
man/meson.build
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
mandir = join_paths(get_option('prefix'), get_option('mandir'))
|
||||||
|
man1dir = join_paths(mandir, 'man1')
|
||||||
|
|
||||||
|
conf = configuration_data()
|
||||||
|
conf.set('version', meson.project_version())
|
||||||
|
conf.set('version_date', version_date)
|
||||||
|
|
||||||
|
manpage = configure_file(
|
||||||
|
input: files('tio.1.in'),
|
||||||
|
output: 'tio.1',
|
||||||
|
configuration: conf)
|
||||||
|
|
||||||
|
install_man(
|
||||||
|
manpage,
|
||||||
|
install_dir: man1dir)
|
||||||
107
man/tio.1
107
man/tio.1
|
|
@ -1,107 +0,0 @@
|
||||||
.TH "tio" "1" "September 2017"
|
|
||||||
|
|
||||||
.SH "NAME"
|
|
||||||
tio \- a simple TTY terminal I/O application
|
|
||||||
|
|
||||||
.SH "SYNOPSIS"
|
|
||||||
.PP
|
|
||||||
.B tio
|
|
||||||
.RI "[" <options> "] " "<tty device>"
|
|
||||||
|
|
||||||
.SH "DESCRIPTION"
|
|
||||||
.PP
|
|
||||||
.B tio
|
|
||||||
is a simple TTY terminal application which features a straightforward
|
|
||||||
commandline interface to easily connect to TTY devices for basic input/output.
|
|
||||||
|
|
||||||
.SH "OPTIONS"
|
|
||||||
|
|
||||||
.TP
|
|
||||||
.BR \-b ", " "\-\-baudrate " \fI<bps>
|
|
||||||
|
|
||||||
Set baud rate [bps] (default: 115200).
|
|
||||||
.TP
|
|
||||||
.BR \-d ", " "\-\-databits 5" | 6 | 7 | 8
|
|
||||||
|
|
||||||
Set data bits (default: 8).
|
|
||||||
.TP
|
|
||||||
.BR \-f ", " "\-\-flow hard" | soft | none
|
|
||||||
|
|
||||||
Set flow control (default: none).
|
|
||||||
.TP
|
|
||||||
.BR \-s ", " "\-\-stopbits 1" | 2
|
|
||||||
|
|
||||||
Set stop bits (default: 1).
|
|
||||||
.TP
|
|
||||||
.BR \-p ", " "\-\-parity odd" | even | none
|
|
||||||
|
|
||||||
Set parity (default: none).
|
|
||||||
.TP
|
|
||||||
.BR \-o ", " "\-\-output\-delay " \fI<ms>
|
|
||||||
|
|
||||||
Set output delay [ms] inserted between each transmitted character (default: 0).
|
|
||||||
.TP
|
|
||||||
.BR \-n ", " \-\-no\-autoconnect
|
|
||||||
|
|
||||||
Disable automatic connect.
|
|
||||||
|
|
||||||
By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost (eg. device disconnects), it will wait for the device to reappear and then reconnect.
|
|
||||||
|
|
||||||
However, if the
|
|
||||||
.B \-\-no\-autoconnect
|
|
||||||
option is provided, tio will exit if the device is not present or an established connection is lost.
|
|
||||||
|
|
||||||
.TP
|
|
||||||
.BR \-l ", " "\-\-log " \fI<filename>
|
|
||||||
|
|
||||||
Log to file.
|
|
||||||
.TP
|
|
||||||
.BR \-v ", " \-\-version
|
|
||||||
|
|
||||||
Display program version.
|
|
||||||
.TP
|
|
||||||
.BR \-h ", " \-\-help
|
|
||||||
|
|
||||||
Display help.
|
|
||||||
|
|
||||||
.SH "KEYS"
|
|
||||||
.PP
|
|
||||||
.TP 16n
|
|
||||||
In session, the following key sequences are intercepted as tio commands:
|
|
||||||
.IP "\fBctrl-t ?"
|
|
||||||
List available key commands
|
|
||||||
.IP "\fBctrl-t b"
|
|
||||||
Send serial break (triggers SysRq on Linux, etc.)
|
|
||||||
.IP "\fBctrl-t c"
|
|
||||||
Show configuration (baudrate, databits, etc.)
|
|
||||||
.IP "\fBctrl-t h"
|
|
||||||
Toggle hexadecimal mode
|
|
||||||
.IP "\fBctrl-t l"
|
|
||||||
Clear screen
|
|
||||||
.IP "\fBctrl-t q"
|
|
||||||
Quit
|
|
||||||
.IP "\fBctrl-t s"
|
|
||||||
Show statistics (total number of bytes transmitted/received)
|
|
||||||
.IP "\fBctrl-t t"
|
|
||||||
Send ctrl-t key code
|
|
||||||
|
|
||||||
.SH "EXAMPLES"
|
|
||||||
.TP
|
|
||||||
Typical use is without options. For example:
|
|
||||||
|
|
||||||
tio /dev/ttyUSB0
|
|
||||||
.TP
|
|
||||||
Which corresponds to the commonly used options:
|
|
||||||
|
|
||||||
tio \-b 115200 \-d 8 \-f none \-s 1 \-p none /dev/ttyUSB0
|
|
||||||
.TP
|
|
||||||
It is recommended to connect serial tty devices by id. For example:
|
|
||||||
|
|
||||||
tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0
|
|
||||||
.PP
|
|
||||||
Using serial devices by id ensures that tio automatically reconnects to the
|
|
||||||
correct serial device if the device is disconnected and then reconnected.
|
|
||||||
|
|
||||||
.SH "AUTHOR"
|
|
||||||
.PP
|
|
||||||
Written by Martin Lund <martin.lund@keep\-it\-simple.com>.
|
|
||||||
781
man/tio.1.in
Normal file
781
man/tio.1.in
Normal file
|
|
@ -0,0 +1,781 @@
|
||||||
|
.TH "tio" "1" "@version_date@" "tio @version@" "User Commands"
|
||||||
|
|
||||||
|
.SH "NAME"
|
||||||
|
tio \- a serial device I/O tool
|
||||||
|
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.PP
|
||||||
|
.B tio
|
||||||
|
.RI "[" <options> "] " "<tty-device|profile|tid>"
|
||||||
|
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
.PP
|
||||||
|
\fBtio\fR is a serial device tool which features a straightforward command-line
|
||||||
|
and configuration file interface to easily connect to serial TTY devices for
|
||||||
|
basic I/O operations.
|
||||||
|
|
||||||
|
.SH "OPTIONS"
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-b ", " "\-\-baudrate " \fI<bps>
|
||||||
|
|
||||||
|
Set baud rate [bps] (default: 115200).
|
||||||
|
.TP
|
||||||
|
.BR \-d ", " "\-\-databits " 5 | 6 | 7 | 8
|
||||||
|
|
||||||
|
Set data bits (default: 8).
|
||||||
|
.TP
|
||||||
|
.BR \-f ", " "\-\-flow " hard | soft | none
|
||||||
|
|
||||||
|
Set flow control (default: none).
|
||||||
|
.TP
|
||||||
|
.BR \-s ", " "\-\-stopbits " 1 | 2
|
||||||
|
|
||||||
|
Set stop bits (default: 1).
|
||||||
|
.TP
|
||||||
|
.BR \-p ", " "\-\-parity " odd | even | none | mark | space
|
||||||
|
|
||||||
|
Set parity (default: none).
|
||||||
|
|
||||||
|
Note: With \fBmark\fR parity the parity bit is always 0. With \fBspace\fR
|
||||||
|
parity the parity bit is always 1. Not all platforms support \fBmark\fR and
|
||||||
|
\fBspace\fR parity.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-o ", " "\-\-output\-delay " \fI<ms>
|
||||||
|
|
||||||
|
Set output delay [ms] inserted between each sent character (default: 0).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-O ", " "\-\-output\-line\-delay " \fI<ms>
|
||||||
|
|
||||||
|
Set output delay [ms] inserted between each sent line (default: 0).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-line\-pulse\-duration " \fI<duration>
|
||||||
|
|
||||||
|
Set the pulse duration [ms] of each serial port line using the following key
|
||||||
|
value pair format in the duration field: <key>=<value>
|
||||||
|
|
||||||
|
Each key represents a serial line. The following keys are available:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.TP 8n
|
||||||
|
.IP \fBDTR
|
||||||
|
Data Terminal Ready
|
||||||
|
.IP \fBRTS
|
||||||
|
Request To Send
|
||||||
|
.IP \fBCTS
|
||||||
|
Clear To Send
|
||||||
|
.IP \fBDSR
|
||||||
|
Data Set Ready
|
||||||
|
.IP \fBDCD
|
||||||
|
Data Carrier Detect
|
||||||
|
.IP \fBRI
|
||||||
|
Ring Indicator
|
||||||
|
.P
|
||||||
|
If defining more than one key value pair, the pairs must be comma separated.
|
||||||
|
|
||||||
|
The default pulse duration for each line is 100 ms.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-a, \-\-auto\-connect new|latest|direct"
|
||||||
|
|
||||||
|
Automatically connect to serial device according to one of the following
|
||||||
|
strategies:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.TP 10n
|
||||||
|
.IP "\fBnew"
|
||||||
|
Automatically connect to first new appearing serial device.
|
||||||
|
.IP "\fBlatest"
|
||||||
|
Automatically connect to latest registered serial device.
|
||||||
|
.IP "\fBdirect"
|
||||||
|
Connect directly to specified TTY device.
|
||||||
|
.P
|
||||||
|
All the listed strategies automatically reconnects according to strategy if
|
||||||
|
device is not available or connection is lost.
|
||||||
|
.P
|
||||||
|
Default value is "direct".
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-exclude\-devices \fI<pattern>"
|
||||||
|
|
||||||
|
Exclude devices by pattern ('*' and '?' supported).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-exclude\-drivers \fI<pattern>"
|
||||||
|
|
||||||
|
Exclude drivers by pattern ('*' and '?' supported).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-exclude\-tids \fI<pattern>"
|
||||||
|
|
||||||
|
Exclude topology IDs by pattern ('*' and '?' supported).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-n ", " \-\-no\-reconnect
|
||||||
|
|
||||||
|
Do not reconnect.
|
||||||
|
|
||||||
|
This means that tio will exit if it fails to connect to device or an
|
||||||
|
established connection is lost.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-e ", " "\-\-local\-echo
|
||||||
|
|
||||||
|
Enable local echo.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-t ", " \-\-timestamp
|
||||||
|
|
||||||
|
Enable line timestamp.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-timestamp\-format \fI<format>"
|
||||||
|
|
||||||
|
Set timestamp format to any of the following timestamp formats:
|
||||||
|
.RS
|
||||||
|
.TP 16n
|
||||||
|
|
||||||
|
.IP "\fB24hour"
|
||||||
|
24-hour format ("hh:mm:ss.sss")
|
||||||
|
.IP "\fB24hour-start"
|
||||||
|
24-hour format relative to start time
|
||||||
|
.IP "\fB24hour-delta"
|
||||||
|
24-hour format relative to previous timestamp
|
||||||
|
.IP "\fBiso8601"
|
||||||
|
ISO8601 format ("YYYY-MM-DDThh:mm:ss.sss")
|
||||||
|
.IP "\fBepoch"
|
||||||
|
Seconds since Unix epoch (1970-01-01)
|
||||||
|
.IP "\fBepoch-usec"
|
||||||
|
Seconds since Unix epoch (1970-01-01) with subdivision in microseconds
|
||||||
|
.PP
|
||||||
|
Default format is \fB24hour\fR
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-timestamp\-timeout \fI<ms>"
|
||||||
|
|
||||||
|
Set timestamp timeout value in milliseconds.
|
||||||
|
|
||||||
|
This value only takes effect in hex output mode where timestamps are only
|
||||||
|
printed after elapsed timeout time of no output activity from tty device.
|
||||||
|
|
||||||
|
Default value is 200.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-l ", " \-\-list
|
||||||
|
|
||||||
|
List available targets (serial devices, TIDs, configuration profiles).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-L ", " \-\-log
|
||||||
|
|
||||||
|
Enable log to file.
|
||||||
|
|
||||||
|
The log file will be automatically named using the following format
|
||||||
|
tio_TARGET_YYYY-MM-DDTHH:MM:SS.log. Target being the command line target such
|
||||||
|
as tty-device, tid, or configuration profile.
|
||||||
|
|
||||||
|
The filename can be manually set using the \-\-log-file option.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-log\-file \fI<filename>
|
||||||
|
|
||||||
|
Set log filename.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-log\-directory \fI<path>
|
||||||
|
|
||||||
|
Set log directory path in which to save automatically named log files.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-log\-append
|
||||||
|
|
||||||
|
Append to log file.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-log-strip
|
||||||
|
|
||||||
|
Strip control characters and escape sequences from log.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-m ", " "\-\-map " \fI<flags>
|
||||||
|
|
||||||
|
Map (replace, translate) characters on input to the serial device or output
|
||||||
|
from the serial device. The following mapping flags are supported:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.TP 12n
|
||||||
|
.IP "\fBICRNL"
|
||||||
|
Map CR to NL on input (unless IGNCR is set)
|
||||||
|
.IP "\fBIGNCR"
|
||||||
|
Ignore CR on input
|
||||||
|
.IP "\fBIFFESCC"
|
||||||
|
Map FF to ESC-c on input
|
||||||
|
.IP "\fBINLCR"
|
||||||
|
Map NL to CR on input
|
||||||
|
.IP "\fBINLCRNL"
|
||||||
|
Map NL to CR-NL on input
|
||||||
|
.IP "\fBICRCRNL"
|
||||||
|
Map CR to CR-NL on input
|
||||||
|
.IP "\fBIMSB2LSB"
|
||||||
|
Map MSB bit order to LSB on input
|
||||||
|
.IP "\fBOCRNL"
|
||||||
|
Map CR to NL on output
|
||||||
|
.IP "\fBODELBS"
|
||||||
|
Map DEL to BS on output
|
||||||
|
.IP "\fBONLCRNL"
|
||||||
|
Map NL to CR-NL on output
|
||||||
|
.IP "\fBOLTU"
|
||||||
|
Map lowercase characters to uppercase on output
|
||||||
|
.IP "\fBONULBRK"
|
||||||
|
Map nul (zero) to send break signal on output
|
||||||
|
.IP "\fBOIGNCR"
|
||||||
|
Ignore CR on output
|
||||||
|
.P
|
||||||
|
If defining more than one flag, the flags must be comma separated.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-input\-mode " normal|hex|line
|
||||||
|
|
||||||
|
Set input mode.
|
||||||
|
|
||||||
|
In normal mode input characters are sent immediately as they are typed.
|
||||||
|
|
||||||
|
In hex input mode bytes can be sent by typing the \fBtwo-character
|
||||||
|
hexadecimal\fR representation of the 1 byte value, e.g.: to send \fI0xA\fR you
|
||||||
|
must type \fI0a\fR or \fI0A\fR.
|
||||||
|
|
||||||
|
In line input mode input characters are sent when you press enter. The only
|
||||||
|
editing feature supported in this mode is backspace.
|
||||||
|
|
||||||
|
Default value is "normal".
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-output\-mode " normal|hex|hexN
|
||||||
|
|
||||||
|
Set output mode.
|
||||||
|
|
||||||
|
In hex mode each incoming byte is printed out as a 1 byte hex value.
|
||||||
|
|
||||||
|
In hexN mode, N is a number less than or equal to 4096 which defines how many
|
||||||
|
hex values will be printed before a line break.
|
||||||
|
|
||||||
|
Default value is "normal".
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-c ", " "\-\-color " 0..255|bold|none|list
|
||||||
|
|
||||||
|
Colorize tio text using ANSI color code value ranging from 0 to 255 or use
|
||||||
|
"none" for no color or use "bold" to apply bold formatting to existing system
|
||||||
|
color.
|
||||||
|
|
||||||
|
Use "list" to print a list of available ANSI color codes.
|
||||||
|
|
||||||
|
Default value is "bold".
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-S ", " "\-\-socket \fI<socket>\fR\fB
|
||||||
|
|
||||||
|
Redirect I/O to socket.
|
||||||
|
|
||||||
|
Any input from clients connected to the socket is sent on the serial port as if
|
||||||
|
entered at the terminal where tio is running (except that \fBctrl-t\fR sequences
|
||||||
|
are not recognized), and any input from the serial port is multiplexed to the
|
||||||
|
terminal and all connected clients.
|
||||||
|
|
||||||
|
Sockets remain open while the serial port is disconnected, and writes will block.
|
||||||
|
|
||||||
|
Various socket types are supported using the following prefixes in the socket field:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.TP 20n
|
||||||
|
.IP "\fBunix:<filename>"
|
||||||
|
Unix Domain Socket (file)
|
||||||
|
.IP "\fBinet:<port>"
|
||||||
|
Internet Socket (network)
|
||||||
|
.IP "\fBinet6:<port>"
|
||||||
|
Internet IPv6 Socket (network)
|
||||||
|
.P
|
||||||
|
If port is 0 or no port is provided default port 3333 is used.
|
||||||
|
.P
|
||||||
|
At present there is a hardcoded limit of 16 clients connected at one time.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-rs\-485"
|
||||||
|
|
||||||
|
Enable RS-485 mode.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR " \-\-rs\-485\-config " \fI<config>
|
||||||
|
|
||||||
|
Set the RS-485 configuration using the following key or key value pair format in
|
||||||
|
the configuration field:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.TP 30n
|
||||||
|
.IP \fBRTS_ON_SEND=value
|
||||||
|
Set logical level (0 or 1) for RTS pin when sending
|
||||||
|
.IP \fBRTS_AFTER_SEND=value
|
||||||
|
Set logical level (0 or 1) for RTS pin after sending
|
||||||
|
.IP \fBRTS_DELAY_BEFORE_SEND=value
|
||||||
|
Set RTS delay (ms) before sending
|
||||||
|
.IP \fBRTS_DELAY_AFTER_SEND=value
|
||||||
|
Set RTS delay (ms) after sending
|
||||||
|
.IP \fBRX_DURING_TX
|
||||||
|
Receive data even while sending data
|
||||||
|
.P
|
||||||
|
If defining more than one key or key value pair, they must be comma separated.
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-\-alert none|bell|blink"
|
||||||
|
|
||||||
|
Set alert action on connect/disconnect.
|
||||||
|
|
||||||
|
It will sound the bell once or blink once on successful connect. Likewise it
|
||||||
|
will sound the bell twice or blink twice on disconnect.
|
||||||
|
|
||||||
|
Default value is "none".
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-\-mute"
|
||||||
|
|
||||||
|
Mute tio messages.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-\-script \fI<string>
|
||||||
|
|
||||||
|
Run script from string.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-\-script\-file \fI<filename>
|
||||||
|
|
||||||
|
Run script from file with filename.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-\-script\-run once|always|never"
|
||||||
|
|
||||||
|
Run script on connect once, always, or never.
|
||||||
|
|
||||||
|
Default value is "always".
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-\-exec \fI<command>
|
||||||
|
|
||||||
|
Execute shell command with I/O redirected to device
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR "\-\-complete-profiles
|
||||||
|
|
||||||
|
Prints profiles (for shell completion)
|
||||||
|
.TP
|
||||||
|
.BR \-v ", " \-\-version
|
||||||
|
|
||||||
|
Display program version.
|
||||||
|
.TP
|
||||||
|
.BR \-h ", " \-\-help
|
||||||
|
|
||||||
|
Display help.
|
||||||
|
.SH "KEY COMMANDS"
|
||||||
|
.PP
|
||||||
|
.TP 16n
|
||||||
|
In session, all key strokes are forwarded to the serial device except the following key sequence: a prefix key (default: ctrl-t) followed by a command key. These sequences are intercepted as key commands:
|
||||||
|
.IP "\fBctrl-t ?"
|
||||||
|
List available key commands
|
||||||
|
.IP "\fBctrl-t b"
|
||||||
|
Send serial break (triggers SysRq on Linux, etc.)
|
||||||
|
.IP "\fBctrl-t c"
|
||||||
|
Show configuration (baudrate, databits, etc.)
|
||||||
|
.IP "\fBctrl-t e"
|
||||||
|
Toggle local echo mode
|
||||||
|
.IP "\fBctrl-t f"
|
||||||
|
Toggle log to file
|
||||||
|
.IP "\fBctrl-t F"
|
||||||
|
Flush data I/O buffers (discard data written but not transmitted and data received but not read)
|
||||||
|
.IP "\fBctrl-t g"
|
||||||
|
Toggle serial port line
|
||||||
|
.IP "\fBctrl-t i"
|
||||||
|
Toggle input mode
|
||||||
|
.IP "\fBctrl-t l"
|
||||||
|
Clear screen
|
||||||
|
.IP "\fBctrl-t L"
|
||||||
|
Show line states (DTR, RTS, CTS, DSR, DCD, RI)
|
||||||
|
.IP "\fBctrl-t m"
|
||||||
|
Change mapping of characters on input or output
|
||||||
|
.IP "\fBctrl-t o"
|
||||||
|
Toggle output mode
|
||||||
|
.IP "\fBctrl-t p"
|
||||||
|
Pulse serial port line
|
||||||
|
.IP "\fBctrl-t q"
|
||||||
|
Quit
|
||||||
|
.IP "\fBctrl-t r"
|
||||||
|
Run script
|
||||||
|
.IP "\fBctrl-t R"
|
||||||
|
Execute shell command with I/O redirected to device
|
||||||
|
.IP "\fBctrl-t s"
|
||||||
|
Show TX/RX statistics
|
||||||
|
.IP "\fBctrl-t t"
|
||||||
|
Toggle line timestamp mode
|
||||||
|
.IP "\fBctrl-t v"
|
||||||
|
Show version
|
||||||
|
.IP "\fBctrl-t x"
|
||||||
|
Send file using the XMODEM-1K or XMODEM-CRC protocol (prompts for file name and protocol)
|
||||||
|
.IP "\fBctrl-t y"
|
||||||
|
Send file using the YMODEM protocol (prompts for file name)
|
||||||
|
.IP "\fBctrl-t ctrl-t"
|
||||||
|
Send ctrl-t character
|
||||||
|
|
||||||
|
.SH "SCRIPT API"
|
||||||
|
.PP
|
||||||
|
Tio suppots Lua scripting to easily automate interaction with the tty device.
|
||||||
|
|
||||||
|
In addition to the standard Lua API tio makes the following functions
|
||||||
|
and variables available:
|
||||||
|
|
||||||
|
.TP 6n
|
||||||
|
|
||||||
|
.IP "\fBtio.expect(pattern, timeout)"
|
||||||
|
Waits for the Lua pattern to match or timeout before continuing.
|
||||||
|
Timeout is in milliseconds, defaults to 0 meaning it will wait forever.
|
||||||
|
|
||||||
|
Returns the captures from the pattern or nil on timeout.
|
||||||
|
|
||||||
|
.IP "\fBtio.read(size, timeout)"
|
||||||
|
Read up to size bytes from serial device. If timeout is 0 or not provided it
|
||||||
|
will wait forever until data is ready to read.
|
||||||
|
|
||||||
|
Returns a string up to size bytes long on success and nil on timeout.
|
||||||
|
|
||||||
|
.IP "\fBtio.readline(timeout)"
|
||||||
|
Read line from serial device. If timeout is 0 or not provided it will wait
|
||||||
|
forever until data is ready to read.
|
||||||
|
|
||||||
|
Returns a string on success and nil on timeout. On timeout a partially read
|
||||||
|
line may be returned as a second return value.
|
||||||
|
|
||||||
|
.IP "\fBtio.write(string)"
|
||||||
|
Write string to serial device.
|
||||||
|
|
||||||
|
Returns the tio table.
|
||||||
|
|
||||||
|
.IP "\fBtio.send(file, protocol)"
|
||||||
|
Send file using x/y-modem protocol.
|
||||||
|
|
||||||
|
Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM.
|
||||||
|
|
||||||
|
.IP "\fBtio.ttysearch()"
|
||||||
|
Search for serial devices.
|
||||||
|
|
||||||
|
Returns a table of number indexed tables, one for each serial device found.
|
||||||
|
Each of these tables contains the serial device information accessible via the
|
||||||
|
following string indexed elements "path", "tid", "uptime", "driver",
|
||||||
|
"description".
|
||||||
|
|
||||||
|
Returns nil if no serial devices are found.
|
||||||
|
|
||||||
|
.IP "\fBtio.set{line=state, ...}"
|
||||||
|
Set state of one or multiple tty modem lines.
|
||||||
|
|
||||||
|
Line can be any of DTR, RTS, CTS, DSR, CD, RI
|
||||||
|
|
||||||
|
State is high, low, or toggle.
|
||||||
|
|
||||||
|
.IP "\fBtio.sleep(seconds)"
|
||||||
|
Sleep for seconds.
|
||||||
|
.IP "\fBtio.msleep(ms)"
|
||||||
|
Sleep for milliseconds.
|
||||||
|
|
||||||
|
.IP "\fBtio.alwaysecho"
|
||||||
|
A boolean value, defaults to true.
|
||||||
|
|
||||||
|
If tio.alwaysecho is false, the result of tio.read, tio.readline or tio.expect
|
||||||
|
will only be returned from the function and not logged or printed.
|
||||||
|
|
||||||
|
If tio.alwaysecho is set to true, reading functions also emit a single
|
||||||
|
timestamp to stdout and log file per options.timestamp and options.log.
|
||||||
|
|
||||||
|
.SH "CONFIGURATION FILE"
|
||||||
|
.PP
|
||||||
|
Options can be set via configuration file using the INI format. \fBtio\fR uses
|
||||||
|
the configuration file first found in the following locations in the order
|
||||||
|
listed:
|
||||||
|
|
||||||
|
.PP
|
||||||
|
.I $XDG_CONFIG_HOME/tio/config
|
||||||
|
.PP
|
||||||
|
.I $HOME/.config/tio/config
|
||||||
|
.PP
|
||||||
|
.I $HOME/.tioconfig
|
||||||
|
|
||||||
|
.PP
|
||||||
|
Labels can be used to group settings into named configuration profiles which
|
||||||
|
can be activated from the command-line when starting tio.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
\fBtio\fR will try to match the user input to a configuration profile by name or by
|
||||||
|
pattern to get the TTY device and other options.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
Options without any label change the default options.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
Any options set via command-line will override options set in the configuration file.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
The following configuration file options are available:
|
||||||
|
|
||||||
|
.TP 25n
|
||||||
|
.IP "\fBpattern"
|
||||||
|
Pattern matching user input. This pattern can be an extended regular expression with a single group.
|
||||||
|
.IP "\fBdevice"
|
||||||
|
TTY device to open. If it contains a "%s" it is substituted with the first group match.
|
||||||
|
.IP "\fBbaudrate"
|
||||||
|
Set baud rate
|
||||||
|
.IP "\fBdatabits"
|
||||||
|
Set data bits
|
||||||
|
.IP "\fBflow"
|
||||||
|
Set flow control
|
||||||
|
.IP "\fBstopbits"
|
||||||
|
Set stop bits
|
||||||
|
.IP "\fBparity"
|
||||||
|
Set parity
|
||||||
|
.IP "\fBoutput-delay"
|
||||||
|
Set output character delay
|
||||||
|
.IP "\fBoutput-line-delay"
|
||||||
|
Set output line delay
|
||||||
|
.IP "\fBline-pulse-duration"
|
||||||
|
Set line pulse duration
|
||||||
|
.IP "\fBno-reconnect"
|
||||||
|
Do not reconnect
|
||||||
|
.IP "\fBlog"
|
||||||
|
Enable log to file
|
||||||
|
.IP "\fBlog-file"
|
||||||
|
Set log filename
|
||||||
|
.IP "\fBlog-directory"
|
||||||
|
Set log directory path in which to save automatically named log files.
|
||||||
|
.IP "\fBlog-append"
|
||||||
|
Append to log file
|
||||||
|
.IP "\fBlog-strip"
|
||||||
|
Enable strip of control and escape sequences from log
|
||||||
|
.IP "\fBlocal-echo"
|
||||||
|
Enable local echo
|
||||||
|
.IP "\fBtimestamp"
|
||||||
|
Enable line timestamp
|
||||||
|
.IP "\fBtimestamp-format"
|
||||||
|
Set timestamp format
|
||||||
|
.IP "\fBtimestamp-timeout"
|
||||||
|
Set timestamp timeout
|
||||||
|
.IP "\fBmap"
|
||||||
|
Map characters on input or output
|
||||||
|
.IP "\fBcolor"
|
||||||
|
Colorize tio text using ANSI color code ranging from 0 to 255
|
||||||
|
.IP "\fBinput-mode"
|
||||||
|
Set input mode
|
||||||
|
.IP "\fBoutput-mode"
|
||||||
|
Set output mode
|
||||||
|
.IP "\fBsocket"
|
||||||
|
Set socket to redirect I/O to
|
||||||
|
.IP "\fBprefix-ctrl-key"
|
||||||
|
Set prefix ctrl key (a..z or 'none', default: t)
|
||||||
|
.IP "\fBrs-485"
|
||||||
|
Enable RS-485 mode
|
||||||
|
.IP "\fBrs-485-config"
|
||||||
|
Set RS-485 configuration
|
||||||
|
.IP "\fBalert"
|
||||||
|
Set alert action on connect/disconnect
|
||||||
|
.IP "\fBmute"
|
||||||
|
Mute tio messages
|
||||||
|
.IP "\fBscript"
|
||||||
|
Run script from string
|
||||||
|
.IP "\fBscript-file"
|
||||||
|
Run script from file
|
||||||
|
.IP "\fBscript-run"
|
||||||
|
Run script on connect
|
||||||
|
.IP "\fBexec"
|
||||||
|
Execute shell command with I/O redirected to device
|
||||||
|
|
||||||
|
.PP
|
||||||
|
It is possible to include the content of other configuration files using the
|
||||||
|
include directive like so: "[include <file>]".
|
||||||
|
|
||||||
|
.SH "CONFIGURATION FILE EXAMPLES"
|
||||||
|
|
||||||
|
.TP
|
||||||
|
To change the default configuration simply set options like so:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.nf
|
||||||
|
.eo
|
||||||
|
[default]
|
||||||
|
baudrate = 9600
|
||||||
|
databits = 8
|
||||||
|
parity = none
|
||||||
|
stopbits = 1
|
||||||
|
color = 10
|
||||||
|
line-pulse-duration = DTR=200,RTS=400
|
||||||
|
.ec
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Named configuration profiles can be added via labels:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.nf
|
||||||
|
.eo
|
||||||
|
[rpi3]
|
||||||
|
device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
|
||||||
|
baudrate = 115200
|
||||||
|
color = 11
|
||||||
|
.ec
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Activate the configuration profile by name:
|
||||||
|
|
||||||
|
$ tio rpi3
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Which is equivalent to:
|
||||||
|
|
||||||
|
$ tio -b 115200 -c 11 /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
|
||||||
|
|
||||||
|
.TP
|
||||||
|
A configuration profile can also be activated by its pattern which supports regular expressions:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.nf
|
||||||
|
.eo
|
||||||
|
[usb-devices]
|
||||||
|
pattern = ^usb([0-9]*)
|
||||||
|
device = /dev/ttyUSB%m1
|
||||||
|
baudrate = 115200
|
||||||
|
.ec
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Activate the configuration profile by pattern match:
|
||||||
|
|
||||||
|
$ tio usb12
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Which becomes equivalent to:
|
||||||
|
|
||||||
|
$ tio -b 115200 /dev/ttyUSB12
|
||||||
|
|
||||||
|
.TP
|
||||||
|
It is also possible to combine use of configuration profile and command-line options. For example:
|
||||||
|
|
||||||
|
$ tio -l -t usb12
|
||||||
|
|
||||||
|
.SH "EXAMPLES"
|
||||||
|
.TP
|
||||||
|
Typical use is without options:
|
||||||
|
|
||||||
|
$ tio /dev/ttyUSB0
|
||||||
|
.TP
|
||||||
|
Which corresponds to the commonly used default options:
|
||||||
|
|
||||||
|
$ tio \-b 115200 \-d 8 \-f none \-s 1 \-p none /dev/ttyUSB0
|
||||||
|
.TP
|
||||||
|
It is recommended to connect serial TTY devices by ID:
|
||||||
|
|
||||||
|
$ tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0
|
||||||
|
.PP
|
||||||
|
Using serial devices by ID ensures that tio automatically reconnects to the
|
||||||
|
correct serial device if it is disconnected and then reconnected.
|
||||||
|
.TP
|
||||||
|
Redirect serial device I/O to Unix file socket for scripting:
|
||||||
|
|
||||||
|
$ tio -S unix:/tmp/tio-socket0 /dev/ttyUSB0
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Then, to issue a command via the file socket simply do:
|
||||||
|
|
||||||
|
$ echo "ls -la" | nc -UN /tmp/tio-socket0 > /dev/null
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Or use the expect command to script an interaction:
|
||||||
|
|
||||||
|
.RS
|
||||||
|
.nf
|
||||||
|
.eo
|
||||||
|
#!/usr/bin/expect -f
|
||||||
|
.sp
|
||||||
|
set timeout -1
|
||||||
|
log_user 0
|
||||||
|
.sp
|
||||||
|
spawn nc -UN /tmp/tio-socket0
|
||||||
|
set uart $spawn_id
|
||||||
|
.sp
|
||||||
|
send -i $uart "date\n"
|
||||||
|
expect -i $uart "prompt> "
|
||||||
|
send -i $uart "ls -la\n"
|
||||||
|
expect -i $uart "prompt> "
|
||||||
|
.ec
|
||||||
|
.fi
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
It is also possible to use tio's own simpler expect/send script functionality to e.g. automate logins:
|
||||||
|
|
||||||
|
$ tio --script 'tio.expect("login: "); tio.write("root\\n"); tio.expect("Password: "); tio.write("root\\n")' /dev/ttyUSB0
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Redirect device I/O to network file socket for remote TTY sharing:
|
||||||
|
|
||||||
|
$ tio --socket inet:4444 /dev/ttyUSB0
|
||||||
|
|
||||||
|
.TP
|
||||||
|
|
||||||
|
Then, use netcat to connect to the shared TTY session over network (assuming tio is hosted on IP 10.0.0.42):
|
||||||
|
|
||||||
|
$ nc -N 10.0.0.42 4444
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Pipe command to the serial device:
|
||||||
|
|
||||||
|
$ echo "ls -la" | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Pipe command to serial device and wait for line response within 1 second:
|
||||||
|
|
||||||
|
$ echo "*IDN?" | tio /dev/ttyACM0 --script "tio.expect('\\r\\n', 1000)" --mute
|
||||||
|
.TP
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Likewise, to pipe data from file to the serial device:
|
||||||
|
|
||||||
|
$ cat data.bin | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Map NL to CR-NL on input from device and DEL to BS on output to device:
|
||||||
|
|
||||||
|
$ tio --map INLCRNL,ODELBS /dev/ttyUSB0
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Enable RS-485 mode:
|
||||||
|
|
||||||
|
$ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0
|
||||||
|
|
||||||
|
.TP
|
||||||
|
Manipulate DTR and RTS lines upon first connect to reset connected microcontroller:
|
||||||
|
|
||||||
|
$ tio --script "tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{RTS=toggle}" --script-run once /dev/ttyUSB0
|
||||||
|
|
||||||
|
.SH "WEBSITE"
|
||||||
|
.PP
|
||||||
|
Visit https://tio.github.io
|
||||||
|
|
||||||
|
.SH "AUTHOR"
|
||||||
|
.PP
|
||||||
|
Maintained by Martin Lund <martin.lund@keep\-it\-simple.com>.
|
||||||
625
man/tio.1.txt
Normal file
625
man/tio.1.txt
Normal file
|
|
@ -0,0 +1,625 @@
|
||||||
|
tio(1) User Commands tio(1)
|
||||||
|
|
||||||
|
NAME
|
||||||
|
tio - a serial device I/O tool
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
tio [<options>] <tty-device|profile|tid>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
tio is a serial device tool which features a straightforward command-line and configuration file interface to easily connect to serial TTY devices for basic I/O operations.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-b, --baudrate <bps>
|
||||||
|
|
||||||
|
Set baud rate [bps] (default: 115200).
|
||||||
|
|
||||||
|
-d, --databits 5|6|7|8
|
||||||
|
|
||||||
|
Set data bits (default: 8).
|
||||||
|
|
||||||
|
-f, --flow hard|soft|none
|
||||||
|
|
||||||
|
Set flow control (default: none).
|
||||||
|
|
||||||
|
-s, --stopbits 1|2
|
||||||
|
|
||||||
|
Set stop bits (default: 1).
|
||||||
|
|
||||||
|
-p, --parity odd|even|none|mark|space
|
||||||
|
|
||||||
|
Set parity (default: none).
|
||||||
|
|
||||||
|
Note: With mark parity the parity bit is always 0. With space parity the parity bit is always 1. Not all platforms support mark and space parity.
|
||||||
|
|
||||||
|
-o, --output-delay <ms>
|
||||||
|
|
||||||
|
Set output delay [ms] inserted between each sent character (default: 0).
|
||||||
|
|
||||||
|
-O, --output-line-delay <ms>
|
||||||
|
|
||||||
|
Set output delay [ms] inserted between each sent line (default: 0).
|
||||||
|
|
||||||
|
--line-pulse-duration <duration>
|
||||||
|
|
||||||
|
Set the pulse duration [ms] of each serial port line using the following key value pair format in the duration field: <key>=<value>
|
||||||
|
|
||||||
|
Each key represents a serial line. The following keys are available:
|
||||||
|
|
||||||
|
DTR Data Terminal Ready
|
||||||
|
|
||||||
|
RTS Request To Send
|
||||||
|
|
||||||
|
CTS Clear To Send
|
||||||
|
|
||||||
|
DSR Data Set Ready
|
||||||
|
|
||||||
|
DCD Data Carrier Detect
|
||||||
|
|
||||||
|
RI Ring Indicator
|
||||||
|
|
||||||
|
If defining more than one key value pair, the pairs must be comma separated.
|
||||||
|
|
||||||
|
The default pulse duration for each line is 100 ms.
|
||||||
|
|
||||||
|
-a, --auto-connect new|latest|direct
|
||||||
|
|
||||||
|
Automatically connect to serial device according to one of the following strategies:
|
||||||
|
|
||||||
|
new Automatically connect to first new appearing serial device.
|
||||||
|
|
||||||
|
latest Automatically connect to latest registered serial device.
|
||||||
|
|
||||||
|
direct Connect directly to specified TTY device.
|
||||||
|
|
||||||
|
All the listed strategies automatically reconnects according to strategy if device is not available or connection is lost.
|
||||||
|
|
||||||
|
Default value is "direct".
|
||||||
|
|
||||||
|
--exclude-devices <pattern>
|
||||||
|
|
||||||
|
Exclude devices by pattern ('*' and '?' supported).
|
||||||
|
|
||||||
|
--exclude-drivers <pattern>
|
||||||
|
|
||||||
|
Exclude drivers by pattern ('*' and '?' supported).
|
||||||
|
|
||||||
|
--exclude-tids <pattern>
|
||||||
|
|
||||||
|
Exclude topology IDs by pattern ('*' and '?' supported).
|
||||||
|
|
||||||
|
-n, --no-reconnect
|
||||||
|
|
||||||
|
Do not reconnect.
|
||||||
|
|
||||||
|
This means that tio will exit if it fails to connect to device or an established connection is lost.
|
||||||
|
|
||||||
|
-e, --local-echo
|
||||||
|
|
||||||
|
Enable local echo.
|
||||||
|
|
||||||
|
-t, --timestamp
|
||||||
|
|
||||||
|
Enable line timestamp.
|
||||||
|
|
||||||
|
--timestamp-format <format>
|
||||||
|
|
||||||
|
Set timestamp format to any of the following timestamp formats:
|
||||||
|
|
||||||
|
24hour 24-hour format ("hh:mm:ss.sss")
|
||||||
|
|
||||||
|
24hour-start 24-hour format relative to start time
|
||||||
|
|
||||||
|
24hour-delta 24-hour format relative to previous timestamp
|
||||||
|
|
||||||
|
iso8601 ISO8601 format ("YYYY-MM-DDThh:mm:ss.sss")
|
||||||
|
|
||||||
|
epoch Seconds since Unix epoch (1970-01-01)
|
||||||
|
|
||||||
|
epoch-usec Seconds since Unix epoch (1970-01-01) with subdivision microseconds
|
||||||
|
|
||||||
|
Default format is 24hour
|
||||||
|
|
||||||
|
--timestamp-timeout <ms>
|
||||||
|
|
||||||
|
Set timestamp timeout value in milliseconds.
|
||||||
|
|
||||||
|
This value only takes effect in hex output mode where timestamps are only printed after elapsed timeout time of no output activity from tty device.
|
||||||
|
|
||||||
|
Default value is 200.
|
||||||
|
|
||||||
|
-l, --list
|
||||||
|
|
||||||
|
List available targets (serial devices, TIDs, configuration profiles).
|
||||||
|
|
||||||
|
-L, --log
|
||||||
|
|
||||||
|
Enable log to file.
|
||||||
|
|
||||||
|
The log file will be automatically named using the following format tio_TARGET_YYYY-MM-DDTHH:MM:SS.log. Target being the command line target such as tty-device, tid, or configuration profile.
|
||||||
|
|
||||||
|
The filename can be manually set using the --log-file option.
|
||||||
|
|
||||||
|
--log-file <filename>
|
||||||
|
|
||||||
|
Set log filename.
|
||||||
|
|
||||||
|
--log-directory <path>
|
||||||
|
|
||||||
|
Set log directory path in which to save automatically named log files.
|
||||||
|
|
||||||
|
--log-append
|
||||||
|
|
||||||
|
Append to log file.
|
||||||
|
|
||||||
|
--log-strip
|
||||||
|
|
||||||
|
Strip control characters and escape sequences from log.
|
||||||
|
|
||||||
|
-m, --map <flags>
|
||||||
|
|
||||||
|
Map (replace, translate) characters on input to the serial device or output from the serial device. The following mapping flags are supported:
|
||||||
|
|
||||||
|
ICRNL Map CR to NL on input (unless IGNCR is set)
|
||||||
|
|
||||||
|
IGNCR Ignore CR on input
|
||||||
|
|
||||||
|
IFFESCC Map FF to ESC-c on input
|
||||||
|
|
||||||
|
INLCR Map NL to CR on input
|
||||||
|
|
||||||
|
INLCRNL Map NL to CR-NL on input
|
||||||
|
|
||||||
|
ICRCRNL Map CR to CR-NL on input
|
||||||
|
|
||||||
|
IMSB2LSB Map MSB bit order to LSB on input
|
||||||
|
|
||||||
|
OCRNL Map CR to NL on output
|
||||||
|
|
||||||
|
ODELBS Map DEL to BS on output
|
||||||
|
|
||||||
|
ONLCRNL Map NL to CR-NL on output
|
||||||
|
|
||||||
|
OLTU Map lowercase characters to uppercase on output
|
||||||
|
|
||||||
|
ONULBRK Map nul (zero) to send break signal on output
|
||||||
|
|
||||||
|
OIGNCR Ignore CR on output
|
||||||
|
|
||||||
|
If defining more than one flag, the flags must be comma separated.
|
||||||
|
|
||||||
|
--input-mode normal|hex|line
|
||||||
|
|
||||||
|
Set input mode.
|
||||||
|
|
||||||
|
In normal mode input characters are sent immediately as they are typed.
|
||||||
|
|
||||||
|
In hex input mode bytes can be sent by typing the two-character hexadecimal representation of the 1 byte value, e.g.: to send 0xA you must type 0a or 0A.
|
||||||
|
|
||||||
|
In line input mode input characters are sent when you press enter. The only editing feature supported in this mode is backspace.
|
||||||
|
|
||||||
|
Default value is "normal".
|
||||||
|
|
||||||
|
--output-mode normal|hex|hexN
|
||||||
|
|
||||||
|
Set output mode.
|
||||||
|
|
||||||
|
In hex mode each incoming byte is printed out as a 1 byte hex value.
|
||||||
|
|
||||||
|
In hexN mode, N is a number less than or equal to 4096 which defines how many hex values will be printed before a line break.
|
||||||
|
|
||||||
|
Default value is "normal".
|
||||||
|
|
||||||
|
-c, --color 0..255|bold|none|list
|
||||||
|
|
||||||
|
Colorize tio text using ANSI color code value ranging from 0 to 255 or use "none" for no color or use "bold" to apply bold formatting to existing system color.
|
||||||
|
|
||||||
|
Use "list" to print a list of available ANSI color codes.
|
||||||
|
|
||||||
|
Default value is "bold".
|
||||||
|
|
||||||
|
-S, --socket <socket>
|
||||||
|
|
||||||
|
Redirect I/O to socket.
|
||||||
|
|
||||||
|
Any input from clients connected to the socket is sent on the serial port as if entered at the terminal where tio is running (except that ctrl-t sequences are not recognized), and any input from the serial port is multi‐
|
||||||
|
plexed to the terminal and all connected clients.
|
||||||
|
|
||||||
|
Sockets remain open while the serial port is disconnected, and writes will block.
|
||||||
|
|
||||||
|
Various socket types are supported using the following prefixes in the socket field:
|
||||||
|
|
||||||
|
unix:<filename> Unix Domain Socket (file)
|
||||||
|
|
||||||
|
inet:<port> Internet Socket (network)
|
||||||
|
|
||||||
|
inet6:<port> Internet IPv6 Socket (network)
|
||||||
|
|
||||||
|
If port is 0 or no port is provided default port 3333 is used.
|
||||||
|
|
||||||
|
At present there is a hardcoded limit of 16 clients connected at one time.
|
||||||
|
|
||||||
|
--rs-485
|
||||||
|
|
||||||
|
Enable RS-485 mode.
|
||||||
|
|
||||||
|
--rs-485-config <config>
|
||||||
|
|
||||||
|
Set the RS-485 configuration using the following key or key value pair format in the configuration field:
|
||||||
|
|
||||||
|
RTS_ON_SEND=value Set logical level (0 or 1) for RTS pin when sending
|
||||||
|
|
||||||
|
RTS_AFTER_SEND=value Set logical level (0 or 1) for RTS pin after sending
|
||||||
|
|
||||||
|
RTS_DELAY_BEFORE_SEND=value Set RTS delay (ms) before sending
|
||||||
|
|
||||||
|
RTS_DELAY_AFTER_SEND=value Set RTS delay (ms) after sending
|
||||||
|
|
||||||
|
RX_DURING_TX Receive data even while sending data
|
||||||
|
|
||||||
|
If defining more than one key or key value pair, they must be comma separated.
|
||||||
|
|
||||||
|
--alert none|bell|blink
|
||||||
|
|
||||||
|
Set alert action on connect/disconnect.
|
||||||
|
|
||||||
|
It will sound the bell once or blink once on successful connect. Likewise it will sound the bell twice or blink twice on disconnect.
|
||||||
|
|
||||||
|
Default value is "none".
|
||||||
|
|
||||||
|
--mute
|
||||||
|
|
||||||
|
Mute tio messages.
|
||||||
|
|
||||||
|
--script <string>
|
||||||
|
|
||||||
|
Run script from string.
|
||||||
|
|
||||||
|
--script-file <filename>
|
||||||
|
|
||||||
|
Run script from file with filename.
|
||||||
|
|
||||||
|
--script-run once|always|never
|
||||||
|
|
||||||
|
Run script on connect once, always, or never.
|
||||||
|
|
||||||
|
Default value is "always".
|
||||||
|
|
||||||
|
--exec <command>
|
||||||
|
|
||||||
|
Execute shell command with I/O redirected to device
|
||||||
|
|
||||||
|
--complete-profiles
|
||||||
|
|
||||||
|
Prints profiles (for shell completion)
|
||||||
|
|
||||||
|
-v, --version
|
||||||
|
|
||||||
|
Display program version.
|
||||||
|
|
||||||
|
-h, --help
|
||||||
|
|
||||||
|
Display help.
|
||||||
|
|
||||||
|
KEY COMMANDS
|
||||||
|
In session, all key strokes are forwarded to the serial device except the following key sequence: a prefix key (default: ctrl-t) followed by a command key. These sequences are intercepted as key commands:
|
||||||
|
|
||||||
|
ctrl-t ? List available key commands
|
||||||
|
|
||||||
|
ctrl-t b Send serial break (triggers SysRq on Linux, etc.)
|
||||||
|
|
||||||
|
ctrl-t c Show configuration (baudrate, databits, etc.)
|
||||||
|
|
||||||
|
ctrl-t e Toggle local echo mode
|
||||||
|
|
||||||
|
ctrl-t f Toggle log to file
|
||||||
|
|
||||||
|
ctrl-t F Flush data I/O buffers (discard data written but not transmitted and data received but not read)
|
||||||
|
|
||||||
|
ctrl-t g Toggle serial port line
|
||||||
|
|
||||||
|
ctrl-t i Toggle input mode
|
||||||
|
|
||||||
|
ctrl-t l Clear screen
|
||||||
|
|
||||||
|
ctrl-t L Show line states (DTR, RTS, CTS, DSR, DCD, RI)
|
||||||
|
|
||||||
|
ctrl-t m Change mapping of characters on input or output
|
||||||
|
|
||||||
|
ctrl-t o Toggle output mode
|
||||||
|
|
||||||
|
ctrl-t p Pulse serial port line
|
||||||
|
|
||||||
|
ctrl-t q Quit
|
||||||
|
|
||||||
|
ctrl-t r Run script
|
||||||
|
|
||||||
|
ctrl-t R Execute shell command with I/O redirected to device
|
||||||
|
|
||||||
|
ctrl-t s Show TX/RX statistics
|
||||||
|
|
||||||
|
ctrl-t t Toggle line timestamp mode
|
||||||
|
|
||||||
|
ctrl-t v Show version
|
||||||
|
|
||||||
|
ctrl-t x Send file using the XMODEM-1K or XMODEM-CRC protocol (prompts for file name and protocol)
|
||||||
|
|
||||||
|
ctrl-t y Send file using the YMODEM protocol (prompts for file name)
|
||||||
|
|
||||||
|
ctrl-t ctrl-t Send ctrl-t character
|
||||||
|
|
||||||
|
SCRIPT API
|
||||||
|
Tio suppots Lua scripting to easily automate interaction with the tty device.
|
||||||
|
|
||||||
|
In addition to the standard Lua API tio makes the following functions available:
|
||||||
|
|
||||||
|
expect(string, timeout)
|
||||||
|
Expect string - waits for string to match or timeout before continuing. Supports regular expressions. Special characters must be escaped with '\\'. Timeout is in milliseconds, defaults to 0 meaning it will wait forever.
|
||||||
|
|
||||||
|
Returns 1 on successful match, 0 on timeout, or -1 on error.
|
||||||
|
|
||||||
|
On successful match it also returns the match string as second return value.
|
||||||
|
|
||||||
|
read(size, timeout)
|
||||||
|
Read up to size bytes from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read.
|
||||||
|
|
||||||
|
Returns number of bytes read on success, 0 on timeout, or -1 on error.
|
||||||
|
|
||||||
|
On success, returns read string as second return value. Also emits a single timestamp to stdout and log file per options.timestamp and options.log.
|
||||||
|
|
||||||
|
read_line(timeout)
|
||||||
|
Read line from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read.
|
||||||
|
|
||||||
|
Returns number of bytes read on success, 0 on timeout, or -1 on error.
|
||||||
|
|
||||||
|
On success, returns the string that was read as second return value. Also emits a single timestamp to stdout and log file per options.timestamp and options.log.
|
||||||
|
|
||||||
|
write(string)
|
||||||
|
Write string to serial device.
|
||||||
|
|
||||||
|
Returns number of bytes written on success or -1 on error.
|
||||||
|
|
||||||
|
send(file, protocol)
|
||||||
|
Send file using x/y-modem protocol.
|
||||||
|
|
||||||
|
Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM.
|
||||||
|
|
||||||
|
tty_search()
|
||||||
|
Search for serial devices.
|
||||||
|
|
||||||
|
Returns a table of number indexed tables, one for each serial device found. Each of these tables contains the serial device information accessible via the following string indexed elements "path", "tid", "uptime", "dri‐
|
||||||
|
ver", "description".
|
||||||
|
|
||||||
|
Returns nil if no serial devices are found.
|
||||||
|
|
||||||
|
set{line=state, ...}
|
||||||
|
Set state of one or multiple tty modem lines.
|
||||||
|
|
||||||
|
Line can be any of DTR, RTS, CTS, DSR, CD, RI
|
||||||
|
|
||||||
|
State is high, low, or toggle.
|
||||||
|
|
||||||
|
sleep(seconds)
|
||||||
|
Sleep for seconds.
|
||||||
|
|
||||||
|
msleep(ms)
|
||||||
|
Sleep for milliseconds.
|
||||||
|
|
||||||
|
exit(code)
|
||||||
|
Exit with exit code.
|
||||||
|
|
||||||
|
CONFIGURATION FILE
|
||||||
|
Options can be set via configuration file using the INI format. tio uses the configuration file first found in the following locations in the order listed:
|
||||||
|
|
||||||
|
$XDG_CONFIG_HOME/tio/config
|
||||||
|
|
||||||
|
$HOME/.config/tio/config
|
||||||
|
|
||||||
|
$HOME/.tioconfig
|
||||||
|
|
||||||
|
Labels can be used to group settings into named configuration profiles which can be activated from the command-line when starting tio.
|
||||||
|
|
||||||
|
tio will try to match the user input to a configuration profile by name or by pattern to get the TTY device and other options.
|
||||||
|
|
||||||
|
Options without any label change the default options.
|
||||||
|
|
||||||
|
Any options set via command-line will override options set in the configuration file.
|
||||||
|
|
||||||
|
The following configuration file options are available:
|
||||||
|
|
||||||
|
pattern Pattern matching user input. This pattern can be an extended regular expression with a single group.
|
||||||
|
|
||||||
|
device TTY device to open. If it contains a "%s" it is substituted with the first group match.
|
||||||
|
|
||||||
|
baudrate Set baud rate
|
||||||
|
|
||||||
|
databits Set data bits
|
||||||
|
|
||||||
|
flow Set flow control
|
||||||
|
|
||||||
|
stopbits Set stop bits
|
||||||
|
|
||||||
|
parity Set parity
|
||||||
|
|
||||||
|
output-delay Set output character delay
|
||||||
|
|
||||||
|
output-line-delay Set output line delay
|
||||||
|
|
||||||
|
line-pulse-duration Set line pulse duration
|
||||||
|
|
||||||
|
no-reconnect Do not reconnect
|
||||||
|
|
||||||
|
log Enable log to file
|
||||||
|
|
||||||
|
log-file Set log filename
|
||||||
|
|
||||||
|
log-directory Set log directory path in which to save automatically named log files.
|
||||||
|
|
||||||
|
log-append Append to log file
|
||||||
|
|
||||||
|
log-strip Enable strip of control and escape sequences from log
|
||||||
|
|
||||||
|
local-echo Enable local echo
|
||||||
|
|
||||||
|
timestamp Enable line timestamp
|
||||||
|
|
||||||
|
timestamp-format Set timestamp format
|
||||||
|
|
||||||
|
timestamp-timeout Set timestamp timeout
|
||||||
|
|
||||||
|
map Map characters on input or output
|
||||||
|
|
||||||
|
color Colorize tio text using ANSI color code ranging from 0 to 255
|
||||||
|
|
||||||
|
input-mode Set input mode
|
||||||
|
|
||||||
|
output-mode Set output mode
|
||||||
|
|
||||||
|
socket Set socket to redirect I/O to
|
||||||
|
|
||||||
|
prefix-ctrl-key Set prefix ctrl key (a..z or 'none', default: t)
|
||||||
|
|
||||||
|
rs-485 Enable RS-485 mode
|
||||||
|
|
||||||
|
rs-485-config Set RS-485 configuration
|
||||||
|
|
||||||
|
alert Set alert action on connect/disconnect
|
||||||
|
|
||||||
|
mute Mute tio messages
|
||||||
|
|
||||||
|
script Run script from string
|
||||||
|
|
||||||
|
script-file Run script from file
|
||||||
|
|
||||||
|
script-run Run script on connect
|
||||||
|
|
||||||
|
exec Execute shell command with I/O redirected to device
|
||||||
|
|
||||||
|
It is possible to include the content of other configuration files using the include directive like so: "[include <file>]".
|
||||||
|
|
||||||
|
CONFIGURATION FILE EXAMPLES
|
||||||
|
To change the default configuration simply set options like so:
|
||||||
|
|
||||||
|
[default]
|
||||||
|
baudrate = 9600
|
||||||
|
databits = 8
|
||||||
|
parity = none
|
||||||
|
stopbits = 1
|
||||||
|
color = 10
|
||||||
|
line-pulse-duration = DTR=200,RTS=400
|
||||||
|
|
||||||
|
Named configuration profiles can be added via labels:
|
||||||
|
|
||||||
|
[rpi3]
|
||||||
|
device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
|
||||||
|
baudrate = 115200
|
||||||
|
color = 11
|
||||||
|
|
||||||
|
Activate the configuration profile by name:
|
||||||
|
|
||||||
|
$ tio rpi3
|
||||||
|
|
||||||
|
Which is equivalent to:
|
||||||
|
|
||||||
|
$ tio -b 115200 -c 11 /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
|
||||||
|
|
||||||
|
A configuration profile can also be activated by its pattern which supports regular expressions:
|
||||||
|
|
||||||
|
[usb-devices]
|
||||||
|
pattern = ^usb([0-9]*)
|
||||||
|
device = /dev/ttyUSB%m1
|
||||||
|
baudrate = 115200
|
||||||
|
|
||||||
|
Activate the configuration profile by pattern match:
|
||||||
|
|
||||||
|
$ tio usb12
|
||||||
|
|
||||||
|
Which becomes equivalent to:
|
||||||
|
|
||||||
|
$ tio -b 115200 /dev/ttyUSB12
|
||||||
|
|
||||||
|
It is also possible to combine use of configuration profile and command-line options. For example:
|
||||||
|
|
||||||
|
$ tio -l -t usb12
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
Typical use is without options:
|
||||||
|
|
||||||
|
$ tio /dev/ttyUSB0
|
||||||
|
|
||||||
|
Which corresponds to the commonly used default options:
|
||||||
|
|
||||||
|
$ tio -b 115200 -d 8 -f none -s 1 -p none /dev/ttyUSB0
|
||||||
|
|
||||||
|
It is recommended to connect serial TTY devices by ID:
|
||||||
|
|
||||||
|
$ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
|
||||||
|
|
||||||
|
Using serial devices by ID ensures that tio automatically reconnects to the correct serial device if it is disconnected and then reconnected.
|
||||||
|
|
||||||
|
Redirect serial device I/O to Unix file socket for scripting:
|
||||||
|
|
||||||
|
$ tio -S unix:/tmp/tio-socket0 /dev/ttyUSB0
|
||||||
|
|
||||||
|
Then, to issue a command via the file socket simply do:
|
||||||
|
|
||||||
|
$ echo "ls -la" | nc -UN /tmp/tio-socket0 > /dev/null
|
||||||
|
|
||||||
|
Or use the expect command to script an interaction:
|
||||||
|
|
||||||
|
#!/usr/bin/expect -f
|
||||||
|
|
||||||
|
set timeout -1
|
||||||
|
log_user 0
|
||||||
|
|
||||||
|
spawn nc -UN /tmp/tio-socket0
|
||||||
|
set uart $spawn_id
|
||||||
|
|
||||||
|
send -i $uart "date\n"
|
||||||
|
expect -i $uart "prompt> "
|
||||||
|
send -i $uart "ls -la\n"
|
||||||
|
expect -i $uart "prompt> "
|
||||||
|
|
||||||
|
It is also possible to use tio's own simpler expect/send script functionality to e.g. automate logins:
|
||||||
|
|
||||||
|
$ tio --script 'expect("login: "); write("root\n"); expect("Password: "); write("root\n")' /dev/ttyUSB0
|
||||||
|
|
||||||
|
Redirect device I/O to network file socket for remote TTY sharing:
|
||||||
|
|
||||||
|
$ tio --socket inet:4444 /dev/ttyUSB0
|
||||||
|
|
||||||
|
Then, use netcat to connect to the shared TTY session over network (assuming tio is hosted on IP 10.0.0.42):
|
||||||
|
|
||||||
|
$ nc -N 10.0.0.42 4444
|
||||||
|
|
||||||
|
Pipe command to the serial device:
|
||||||
|
|
||||||
|
$ echo "ls -la" | tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
|
||||||
|
|
||||||
|
Pipe command to serial device and wait for line response within 1 second:
|
||||||
|
|
||||||
|
$ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\r\n', 1000)" --mute
|
||||||
|
|
||||||
|
Likewise, to pipe data from file to the serial device:
|
||||||
|
|
||||||
|
$ cat data.bin | tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0
|
||||||
|
|
||||||
|
Map NL to CR-NL on input from device and DEL to BS on output to device:
|
||||||
|
|
||||||
|
$ tio --map INLCRNL,ODELBS /dev/ttyUSB0
|
||||||
|
|
||||||
|
Enable RS-485 mode:
|
||||||
|
|
||||||
|
$ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0
|
||||||
|
|
||||||
|
Manipulate DTR and RTS lines upon first connect to reset connected microcontroller:
|
||||||
|
|
||||||
|
$ tio --script "set{DTR=high,RTS=low}; msleep(100); set{RTS=toggle}" --script-run once /dev/ttyUSB0
|
||||||
|
|
||||||
|
WEBSITE
|
||||||
|
Visit https://tio.github.io
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Maintained by Martin Lund <martin.lund@keep-it-simple.com>.
|
||||||
|
|
||||||
|
tio 3.9 2025-04-13 tio(1)
|
||||||
87
meson.build
Normal file
87
meson.build
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
project('tio', 'c',
|
||||||
|
version : '3.9',
|
||||||
|
license : 'GPL-2.0-or-later',
|
||||||
|
meson_version : '>= 0.53.2',
|
||||||
|
default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ]
|
||||||
|
)
|
||||||
|
|
||||||
|
# The tag date of the project_version(), update when the version bumps.
|
||||||
|
version_date = '2025-04-13'
|
||||||
|
|
||||||
|
# Test for dynamic baudrate configuration interface
|
||||||
|
compiler = meson.get_compiler('c')
|
||||||
|
enable_setspeed2 = false
|
||||||
|
enable_iossiospeed = false
|
||||||
|
if host_machine.system() != 'darwin'
|
||||||
|
if compiler.check_header('asm-generic/ioctls.h')
|
||||||
|
enable_setspeed2 = compiler.has_header_symbol('asm-generic/ioctls.h', 'TCGETS2')
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
if compiler.check_header('IOKit/serial/ioss.h')
|
||||||
|
enable_iossiospeed = compiler.has_header_symbol('IOKit/serial/ioss.h', 'IOSSIOSPEED')
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Test for supported baudrates
|
||||||
|
test_baudrates = [
|
||||||
|
0,
|
||||||
|
50,
|
||||||
|
75,
|
||||||
|
110,
|
||||||
|
134,
|
||||||
|
150,
|
||||||
|
200,
|
||||||
|
300,
|
||||||
|
600,
|
||||||
|
1200,
|
||||||
|
1800,
|
||||||
|
2400,
|
||||||
|
4800,
|
||||||
|
7200,
|
||||||
|
9600,
|
||||||
|
14400,
|
||||||
|
19200,
|
||||||
|
28800,
|
||||||
|
38400,
|
||||||
|
57600,
|
||||||
|
76800,
|
||||||
|
115200,
|
||||||
|
230400,
|
||||||
|
460800,
|
||||||
|
500000,
|
||||||
|
576000,
|
||||||
|
921600,
|
||||||
|
1000000,
|
||||||
|
1152000,
|
||||||
|
1500000,
|
||||||
|
2000000,
|
||||||
|
2500000,
|
||||||
|
3000000,
|
||||||
|
3500000,
|
||||||
|
4000000 ]
|
||||||
|
|
||||||
|
baudrates = ''
|
||||||
|
baudrate_cases = ''
|
||||||
|
foreach rate : test_baudrates
|
||||||
|
baudrate = rate.to_string()
|
||||||
|
value = compiler.get_define('B' + baudrate, prefix: '#include <termios.h>')
|
||||||
|
if value != ''
|
||||||
|
baudrates = baudrates + baudrate + ' '
|
||||||
|
baudrate_cases = baudrate_cases + ' case ' + baudrate + ': baudrate = B' + baudrate + '; break;'
|
||||||
|
endif
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
# Test for RS-485 support on Linux
|
||||||
|
enable_rs485 = false
|
||||||
|
if host_machine.system() == 'linux'
|
||||||
|
if compiler.check_header('linux/serial.h')
|
||||||
|
enable_rs485 = compiler.has_header_symbol('sys/ioctl.h', 'TIOCSRS485')
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
subdir('src')
|
||||||
|
|
||||||
|
install_man_pages = get_option('install_man_pages')
|
||||||
|
if install_man_pages
|
||||||
|
subdir('man')
|
||||||
|
endif
|
||||||
6
meson_options.txt
Normal file
6
meson_options.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
option('bashcompletiondir',
|
||||||
|
type : 'string',
|
||||||
|
description : 'Directory for bash completion scripts ["no" disables]')
|
||||||
|
option('install_man_pages',
|
||||||
|
type : 'boolean', value: true,
|
||||||
|
description : 'Install man pages')
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
AM_CFLAGS = -Wall
|
|
||||||
bin_PROGRAMS = tio
|
|
||||||
tio_SOURCES = tty.c \
|
|
||||||
options.c \
|
|
||||||
time.c \
|
|
||||||
main.c \
|
|
||||||
log.c \
|
|
||||||
error.c \
|
|
||||||
include/tio/tty.h \
|
|
||||||
include/tio/options.h \
|
|
||||||
include/tio/time.h \
|
|
||||||
include/tio/print.h \
|
|
||||||
include/tio/log.h \
|
|
||||||
include/tio/error.h
|
|
||||||
|
|
||||||
if ENABLE_BASH_COMPLETION
|
|
||||||
bashcompletiondir=@BASH_COMPLETION_DIR@
|
|
||||||
bashcompletion_DATA=bash-completion/tio
|
|
||||||
endif
|
|
||||||
83
src/alert.c
Normal file
83
src/alert.c
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "options.h"
|
||||||
|
#include "alert.h"
|
||||||
|
|
||||||
|
void blink_background(void)
|
||||||
|
{
|
||||||
|
// Turn on reverse video
|
||||||
|
printf("\e[?5h");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
usleep(200*1000);
|
||||||
|
|
||||||
|
// Turn on normal video
|
||||||
|
printf("\e[?5l");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sound_bell(void)
|
||||||
|
{
|
||||||
|
// Audio bell
|
||||||
|
printf("\a");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void alert_connect(void)
|
||||||
|
{
|
||||||
|
switch (option.alert)
|
||||||
|
{
|
||||||
|
case ALERT_NONE:
|
||||||
|
break;
|
||||||
|
case ALERT_BELL:
|
||||||
|
sound_bell();
|
||||||
|
break;
|
||||||
|
case ALERT_BLINK:
|
||||||
|
blink_background();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void alert_disconnect(void)
|
||||||
|
{
|
||||||
|
switch (option.alert)
|
||||||
|
{
|
||||||
|
case ALERT_NONE:
|
||||||
|
break;
|
||||||
|
case ALERT_BELL:
|
||||||
|
sound_bell();
|
||||||
|
usleep(200*1000);
|
||||||
|
sound_bell();
|
||||||
|
break;
|
||||||
|
case ALERT_BLINK:
|
||||||
|
blink_background();
|
||||||
|
usleep(200*1000);
|
||||||
|
blink_background();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/alert.h
Normal file
33
src/alert.h
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
ALERT_NONE,
|
||||||
|
ALERT_BELL,
|
||||||
|
ALERT_BLINK,
|
||||||
|
ALERT_END,
|
||||||
|
} alert_t;
|
||||||
|
|
||||||
|
void alert_connect(void);
|
||||||
|
void alert_disconnect(void);
|
||||||
18
src/bash-completion/meson.build
Normal file
18
src/bash-completion/meson.build
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
conf = configuration_data()
|
||||||
|
conf.set('baudrates', baudrates)
|
||||||
|
|
||||||
|
completion_file = configure_file( input: files('tio.in'),
|
||||||
|
output: 'tio',
|
||||||
|
configuration: conf )
|
||||||
|
|
||||||
|
bashcompletiondir = get_option('bashcompletiondir')
|
||||||
|
if bashcompletiondir == ''
|
||||||
|
bash_completion_dep = dependency('bash-completion', required: false)
|
||||||
|
if bash_completion_dep.found()
|
||||||
|
bashcompletiondir = join_paths(get_option('datadir'), 'bash-completion', 'completions')
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
if (bashcompletiondir != 'no') and (bashcompletiondir != '')
|
||||||
|
install_data(completion_file, install_dir: bashcompletiondir)
|
||||||
|
endif
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
_tio()
|
_tio()
|
||||||
{
|
{
|
||||||
local cur prev opts base
|
local cur prev opts base ttys
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
|
@ -16,15 +16,44 @@ _tio()
|
||||||
-s --stopbits \
|
-s --stopbits \
|
||||||
-p --parity \
|
-p --parity \
|
||||||
-o --output-delay \
|
-o --output-delay \
|
||||||
-n --no-autoconnect \
|
-o --output-line-delay \
|
||||||
|
--line-pulse-duration \
|
||||||
|
-a --auto-connect \
|
||||||
|
--exclude-devices \
|
||||||
|
--exclude-drivers \
|
||||||
|
--exclude-tids \
|
||||||
|
-n --no-reconnect \
|
||||||
|
-e --local-echo \
|
||||||
-l --log \
|
-l --log \
|
||||||
|
--log-file \
|
||||||
|
--log-directory \
|
||||||
|
--log-append \
|
||||||
|
--log-strip \
|
||||||
|
-m --map \
|
||||||
|
-t --timestamp \
|
||||||
|
--timestamp-format \
|
||||||
|
--timestamp-timeout \
|
||||||
|
-L --list \
|
||||||
|
-c --color \
|
||||||
|
-S --socket \
|
||||||
|
--input-mode \
|
||||||
|
--output-mode \
|
||||||
|
--rs-485 \
|
||||||
|
--rs-485-config \
|
||||||
|
--alert \
|
||||||
|
--mute \
|
||||||
|
--script \
|
||||||
|
--script-file \
|
||||||
|
--script-run \
|
||||||
|
--exec \
|
||||||
|
--complete-profiles \
|
||||||
-v --version \
|
-v --version \
|
||||||
-h --help"
|
-h --help"
|
||||||
|
|
||||||
# Complete the arguments to the options.
|
# Complete the arguments to the options.
|
||||||
case "${prev}" in
|
case "${prev}" in
|
||||||
-b | --baudrate)
|
-b | --baudrate)
|
||||||
local baudrates="@BAUDRATES@"
|
local baudrates="@baudrates@"
|
||||||
COMPREPLY=( $(compgen -W "$baudrates" -- ${cur}) )
|
COMPREPLY=( $(compgen -W "$baudrates" -- ${cur}) )
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
|
@ -45,31 +74,72 @@ _tio()
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
-o | --output-delay)
|
-o | --output-delay)
|
||||||
COMPREPLY=( $(compgen -W "0 1 10 100" -- ${cur}) )
|
COMPREPLY=( $(compgen -W "1 10 100" -- ${cur}) )
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
-n | --no-autoconnect)
|
-O | --output-line-delay)
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
COMPREPLY=( $(compgen -W "1 10 100" -- ${cur}) )
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
-l | --log)
|
-a | --auto-connect)
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
COMPREPLY=( $(compgen -W "new latest none" -- ${cur}) )
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
-v | --version)
|
-m | --map)
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR IFFESCC INLCRNL ICRCRNL IMSB2LSB OCRNL ODELBS ONLCRNL OLTU ONULBRK OIGNCR" -- ${cur}) )
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
-h | --help)
|
--timestamp-format)
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
COMPREPLY=( $(compgen -W "24hour 24hour-start 24hour-delta iso8601 epoch epoch-usec" -- ${cur}) )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-c | --color)
|
||||||
|
COMPREPLY=( $(compgen -W "$(seq 0 255) none list" -- ${cur}) )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-S | --socket)
|
||||||
|
COMPREPLY=( $(compgen -W "unix: inet: inet6:" -- ${cur}) )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--input-mode)
|
||||||
|
COMPREPLY=( $(compgen -W "normal hex line" -- ${cur}) )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--output-mode)
|
||||||
|
COMPREPLY=( $(compgen -W "normal hex" -- ${cur}) )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--rs-485-config)
|
||||||
|
COMPREPLY=( $(compgen -W "RTS_ON_SEND RTS_AFTER_SEND RTS_DELAY_BEFORE_SEND RTS_DELAY_AFTER_SEND RX_DURING_TX" -- ${cur}) )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--alert)
|
||||||
|
COMPREPLY=( $(compgen -W "none bell blink" -- ${cur}) )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--script-run)
|
||||||
|
COMPREPLY=( $(compgen -W "once always never" -- ${cur}) )
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
case "${cur}" in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
|
profiles="`tio --complete-profiles`"
|
||||||
return 0
|
|
||||||
|
if [ -d /dev/serial/by-id ]; then
|
||||||
|
ttys=$(printf '%s\n' /dev/tty* /dev/serial/by-id/*)
|
||||||
|
else
|
||||||
|
ttys=$(printf '%s\n' /dev/tty*)
|
||||||
|
fi
|
||||||
|
COMPREPLY=( $(compgen -W "${ttys} ${profiles}" -- ${cur}) )
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Bind completion to tio command
|
# Bind completion to tio command
|
||||||
|
|
|
||||||
788
src/configfile.c
Normal file
788
src/configfile.c
Normal file
|
|
@ -0,0 +1,788 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-2022 Liam Beguin
|
||||||
|
* Copyright (c) 2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <glib.h>
|
||||||
|
#include "configfile.h"
|
||||||
|
#include "timestamp.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "rs485.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
#define CONFIG_GROUP_NAME_DEFAULT "default"
|
||||||
|
#define CONFIG_GROUP_INCLUDE_PREFIX "include "
|
||||||
|
#define MAX_LINE_LENGTH 1024
|
||||||
|
|
||||||
|
struct config_t config = {};
|
||||||
|
|
||||||
|
static void config_file_load(const char *filename, GString *buffer, bool test);
|
||||||
|
static void config_file_process(const char *filename, GString *buffer, GList **included_files, bool test);
|
||||||
|
|
||||||
|
static void config_get_string(GKeyFile *key_file, gchar *group, gchar *key, char **dest, char *allowed_string, ...)
|
||||||
|
{
|
||||||
|
(void)dest;
|
||||||
|
GError *error = NULL;
|
||||||
|
bool mismatch = true;
|
||||||
|
|
||||||
|
gchar *string = g_key_file_get_string(key_file, group, key, &error);
|
||||||
|
if (error != NULL)
|
||||||
|
{
|
||||||
|
if (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)
|
||||||
|
{
|
||||||
|
// Key not found - ignore key
|
||||||
|
g_error_free(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tio_error_print("%s: %s", config.path, error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
const char* current_arg = allowed_string;
|
||||||
|
va_start(args, allowed_string);
|
||||||
|
|
||||||
|
if (current_arg == NULL)
|
||||||
|
{
|
||||||
|
mismatch = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through variable arguments
|
||||||
|
while (current_arg != NULL)
|
||||||
|
{
|
||||||
|
if (strcmp(string, current_arg) == 0)
|
||||||
|
{
|
||||||
|
mismatch = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current_arg = va_arg(args, const char *);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mismatch)
|
||||||
|
{
|
||||||
|
tio_error_print("%s: Invalid %s value '%s' in %s profile", config.path, key, string, group);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
*dest = string;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_get_integer(GKeyFile *key_file, gchar *group, gchar *key, int *dest, int min, int max)
|
||||||
|
{
|
||||||
|
(void)dest;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
int value = g_key_file_get_integer(key_file, group, key, &error);
|
||||||
|
if (error != NULL)
|
||||||
|
{
|
||||||
|
if (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)
|
||||||
|
{
|
||||||
|
// Key not found - ignore key
|
||||||
|
g_error_free(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tio_error_print("%s: %s", config.path, error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((value < min) || (value > max))
|
||||||
|
{
|
||||||
|
tio_error_print("%s: Invalid %s value '%d' in %s profile", config.path, key, value, group);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
*dest = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_get_bool(GKeyFile *key_file, gchar *group, gchar *key, bool *dest)
|
||||||
|
{
|
||||||
|
(void)dest;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
bool value = g_key_file_get_boolean(key_file, group, key, &error);
|
||||||
|
if (error != NULL)
|
||||||
|
{
|
||||||
|
if (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND)
|
||||||
|
{
|
||||||
|
// Key not found - ignore key
|
||||||
|
g_error_free(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tio_error_print("%s: %s", config.path, error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
*dest = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_parse_keys(GKeyFile *key_file, char *group)
|
||||||
|
{
|
||||||
|
char *string = NULL;
|
||||||
|
|
||||||
|
config_get_string(key_file, group, "device", &config.device, NULL);
|
||||||
|
config_get_integer(key_file, group, "baudrate", &option.baudrate, 0, INT_MAX);
|
||||||
|
config_get_integer(key_file, group, "databits", &option.databits, 5, 8);
|
||||||
|
config_get_string(key_file, group, "flow", &string, "none", "hard", "soft", NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
option_parse_flow(string, &option.flow);
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
|
config_get_integer(key_file, group, "stopbits", &option.stopbits, 1, 2);
|
||||||
|
config_get_string(key_file, group, "parity", &string, "odd", "even", "none", "mark", "space", NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
option_parse_parity(string, &option.parity);
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
config_get_integer(key_file, group, "output-delay", &option.output_delay, 0, INT_MAX);
|
||||||
|
config_get_integer(key_file, group, "output-line-delay", &option.output_line_delay, 0, INT_MAX);
|
||||||
|
config_get_string(key_file, group, "line-pulse-duration", &string, NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
option_parse_line_pulse_duration(string);
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
|
config_get_string(key_file, group, "auto-connect", &string, "new", "latest", "direct", NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
option_parse_auto_connect(string, &option.auto_connect);
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
|
config_get_string(key_file, group, "exclude-devices", &option.exclude_devices, NULL);
|
||||||
|
config_get_string(key_file, group, "exclude-drivers", &option.exclude_devices, NULL);
|
||||||
|
config_get_string(key_file, group, "exclude-tids", &option.exclude_devices, NULL);
|
||||||
|
config_get_bool(key_file, group, "no-reconnect", &option.no_reconnect);
|
||||||
|
config_get_bool(key_file, group, "local-echo", &option.local_echo);
|
||||||
|
config_get_string(key_file, group, "input-mode", &string, "normal", "hex", "line", NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
option_parse_input_mode(string, &option.input_mode);
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
|
config_get_string(key_file, group, "output-mode", &string, NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
option_parse_output_mode(string, &option.output_mode);
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
|
config_get_bool(key_file, group, "timestamp", (bool*) &option.timestamp);
|
||||||
|
if (option.timestamp != TIMESTAMP_NONE)
|
||||||
|
{
|
||||||
|
config_get_string(key_file, group, "timestamp-format", &string, "24hour", "24hour-start", "24hour-delta", "iso8601", "epoch", "epoch-usec", NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
option_parse_timestamp(string, &option.timestamp);
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config_get_integer(key_file, group, "timestamp-timeout", &option.timestamp_timeout, 0, INT_MAX);
|
||||||
|
config_get_bool(key_file, group, "log", &option.log);
|
||||||
|
config_get_string(key_file, group, "log-file", &option.log_filename, NULL);
|
||||||
|
config_get_string(key_file, group, "log-directory", &option.log_directory, NULL);
|
||||||
|
config_get_bool(key_file, group, "log-append", &option.log_append);
|
||||||
|
config_get_bool(key_file, group, "log-strip", &option.log_strip);
|
||||||
|
config_get_string(key_file, group, "map", &string, NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
option_parse_mappings(string);
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
|
config_get_string(key_file, group, "color", &string, NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
if (strcmp(string, "list") == 0)
|
||||||
|
{
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
else if (strcmp(string, "none") == 0)
|
||||||
|
{
|
||||||
|
option.color = -1; // No color
|
||||||
|
}
|
||||||
|
else if (strcmp(string, "bold") == 0)
|
||||||
|
{
|
||||||
|
option.color = 256; // Bold
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
option.color = atoi(string);
|
||||||
|
if ((option.color < 0) || (option.color > 255))
|
||||||
|
{
|
||||||
|
tio_error_print("%s: Invalid color value in %s profile", config.path, group);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
|
config_get_string(key_file, group, "socket", &option.socket, NULL);
|
||||||
|
config_get_bool(key_file, group, "rs-485", &option.rs485);
|
||||||
|
config_get_string(key_file, group, "rs-385-config", &string, NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
rs485_parse_config(string);
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
|
config_get_string(key_file, group, "alert", &string, "bell", "blink", "none", NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
option_parse_alert(string, &option.alert);
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
|
config_get_bool(key_file, group, "mute", &option.mute);
|
||||||
|
config_get_string(key_file, group, "script", &option.script, NULL);
|
||||||
|
config_get_string(key_file, group, "script-file", &option.script_filename, NULL);
|
||||||
|
config_get_string(key_file, group, "script-run", &string, NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
option_parse_script_run(string, &option.script_run);
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
|
config_get_string(key_file, group, "exec", &option.exec, NULL);
|
||||||
|
config_get_string(key_file, group, "prefix-ctrl-key", &string, NULL);
|
||||||
|
if (string != NULL)
|
||||||
|
{
|
||||||
|
if (strcmp(string, "none") == 0)
|
||||||
|
{
|
||||||
|
option.prefix_enabled = false;
|
||||||
|
}
|
||||||
|
else if (strlen(string) >= 2)
|
||||||
|
{
|
||||||
|
tio_error_print("%s: Invalid prefix-ctrl-key value in %s profile", config.path, group);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
else if (ctrl_key_code(string[0]) > 0)
|
||||||
|
{
|
||||||
|
option.prefix_enabled = true;
|
||||||
|
option.prefix_code = ctrl_key_code(string[0]);
|
||||||
|
option.prefix_key = string[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tio_error_print("%s: Invalid prefix-ctrl-key value in %s profile", config.path, group);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
g_free((void *)string);
|
||||||
|
string = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_file_resolve(void)
|
||||||
|
{
|
||||||
|
char *xdg = getenv("XDG_CONFIG_HOME");
|
||||||
|
if (xdg)
|
||||||
|
{
|
||||||
|
if (asprintf(&config.path, "%s/tio/config", xdg) != -1)
|
||||||
|
{
|
||||||
|
if (access(config.path, F_OK) == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
free(config.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *home = getenv("HOME");
|
||||||
|
if (home)
|
||||||
|
{
|
||||||
|
if (asprintf(&config.path, "%s/.config/tio/config", home) != -1)
|
||||||
|
{
|
||||||
|
if (access(config.path, F_OK) == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
free(config.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asprintf(&config.path, "%s/.tioconfig", home) != -1)
|
||||||
|
{
|
||||||
|
if (access(config.path, F_OK) == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
free(config.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.path = NULL;
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_file_show_profiles(void)
|
||||||
|
{
|
||||||
|
GString *config_buffer;
|
||||||
|
GError *error = NULL;
|
||||||
|
GKeyFile *keyfile;
|
||||||
|
|
||||||
|
// Reset configuration
|
||||||
|
memset(&config, 0, sizeof(struct config_t));
|
||||||
|
|
||||||
|
// Find config file
|
||||||
|
if (config_file_resolve() != 0)
|
||||||
|
{
|
||||||
|
// None found - stop parsing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load content of configuration file into buffer
|
||||||
|
config_buffer = g_string_new(NULL);
|
||||||
|
config_file_load(config.path, config_buffer, false);
|
||||||
|
|
||||||
|
// Load configuration
|
||||||
|
keyfile = g_key_file_new();
|
||||||
|
if (g_key_file_load_from_data(keyfile, config_buffer->str, config_buffer->len, G_KEY_FILE_NONE, &error) == false)
|
||||||
|
{
|
||||||
|
g_error_free(error);
|
||||||
|
goto error_load;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all group names
|
||||||
|
gsize num_groups;
|
||||||
|
gchar **group = g_key_file_get_groups(keyfile, &num_groups);
|
||||||
|
|
||||||
|
for (gsize i = 0; i < num_groups; i++)
|
||||||
|
{
|
||||||
|
// Skip default group
|
||||||
|
if (strcmp(group[i], CONFIG_GROUP_NAME_DEFAULT) == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip group with include directive
|
||||||
|
if (strncmp(group[i], CONFIG_GROUP_INCLUDE_PREFIX, strlen(CONFIG_GROUP_INCLUDE_PREFIX)) == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s ", group[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev(group);
|
||||||
|
error_load:
|
||||||
|
g_key_file_free(keyfile);
|
||||||
|
g_string_free(config_buffer, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void replace_substring(char *str, const char *substr, const char *replacement)
|
||||||
|
{
|
||||||
|
char *pos = strstr(str, substr);
|
||||||
|
if (pos != NULL)
|
||||||
|
{
|
||||||
|
int substrLen = strlen(substr);
|
||||||
|
int replacementLen = strlen(replacement);
|
||||||
|
memmove(pos + replacementLen, pos + substrLen, strlen(pos + substrLen) + 1);
|
||||||
|
memcpy(pos, replacement, replacementLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *match_and_replace(const char *str, const char *pattern, char *device)
|
||||||
|
{
|
||||||
|
char replacement_str[PATH_MAX] = {};
|
||||||
|
char m_key[14] = {};
|
||||||
|
regex_t regex;
|
||||||
|
|
||||||
|
assert(str != NULL);
|
||||||
|
assert(pattern != NULL);
|
||||||
|
assert(device != NULL);
|
||||||
|
|
||||||
|
char *string = calloc(PATH_MAX, 1);
|
||||||
|
if (string == NULL)
|
||||||
|
{
|
||||||
|
tio_debug_printf("Failure allocating string memory\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
strncpy(string, device, PATH_MAX - 1);
|
||||||
|
|
||||||
|
/* Find matches of pattern in str. For each match, replace any '%mN' in the
|
||||||
|
* copy of the device string with the corresponding match subexpression and
|
||||||
|
* return the new formed device string.
|
||||||
|
*
|
||||||
|
* Note: %m0 = Full match expression.
|
||||||
|
* %m1 = First subexpression
|
||||||
|
* %m2 = Second subexpression
|
||||||
|
* %m3 = etc..
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (regcomp(®ex, pattern, REG_EXTENDED) != 0)
|
||||||
|
{
|
||||||
|
// Failure to compile regular expression
|
||||||
|
tio_error_print("Failure compiling regular expression '%s'\n", pattern);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
regmatch_t matches[regex.re_nsub + 1];
|
||||||
|
int status = regexec(®ex, str, regex.re_nsub + 1, matches, 0);
|
||||||
|
if (status == 0)
|
||||||
|
{
|
||||||
|
tio_debug_printf("Full match: ");
|
||||||
|
int j = 0;
|
||||||
|
for (int i = matches[0].rm_so; i < matches[0].rm_eo; i++)
|
||||||
|
{
|
||||||
|
tio_debug_printf_raw("%c", str[i]);
|
||||||
|
replacement_str[j++] = str[i];
|
||||||
|
}
|
||||||
|
replacement_str[j] = '\0';
|
||||||
|
replace_substring(string, "%m0", replacement_str);
|
||||||
|
tio_debug_printf_raw("\n");
|
||||||
|
|
||||||
|
for (int i = 1; i < ((int)regex.re_nsub + 1) && matches[i].rm_so != -1; i++)
|
||||||
|
{
|
||||||
|
tio_debug_printf("Subexpression %d match: ", i);
|
||||||
|
int k = 0;
|
||||||
|
for (int l = matches[i].rm_so; l < matches[i].rm_eo; l++)
|
||||||
|
{
|
||||||
|
tio_debug_printf_raw("%c", str[l]);
|
||||||
|
replacement_str[k++] = str[l];
|
||||||
|
}
|
||||||
|
replacement_str[k] = '\0';
|
||||||
|
sprintf(m_key, "%%m%d", i);
|
||||||
|
replace_substring(string, m_key, replacement_str);
|
||||||
|
tio_debug_printf_raw("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (status == REG_NOMATCH)
|
||||||
|
{
|
||||||
|
tio_debug_printf("No regex match\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char error_message[100];
|
||||||
|
regerror(status, ®ex, error_message, sizeof(error_message));
|
||||||
|
tio_debug_printf("Regex match failed: %s", error_message);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
regfree(®ex);
|
||||||
|
return string;
|
||||||
|
|
||||||
|
error:
|
||||||
|
regfree(®ex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_file_process_line(const char *line, GString *buffer, GList **included_files, bool test)
|
||||||
|
{
|
||||||
|
if (strncmp(line, "[include ", 9) == 0 && line[strlen(line) - 2] == ']')
|
||||||
|
{
|
||||||
|
char include_filename[MAX_LINE_LENGTH];
|
||||||
|
|
||||||
|
// Construct the format string safely
|
||||||
|
char format_string[50];
|
||||||
|
snprintf(format_string, sizeof(format_string), "[include %%%ds]", MAX_LINE_LENGTH - 1);
|
||||||
|
|
||||||
|
int ret = sscanf(line, format_string, include_filename);
|
||||||
|
if (ret != 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the trailing ']' character
|
||||||
|
include_filename[strlen(include_filename) - 1] = '\0';
|
||||||
|
|
||||||
|
if (g_list_find_custom(*included_files, include_filename, (GCompareFunc)strcmp) != NULL)
|
||||||
|
{
|
||||||
|
// Already included, avoid recursion
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to included files list
|
||||||
|
*included_files = g_list_append(*included_files, g_strdup(include_filename));
|
||||||
|
|
||||||
|
// Process the included file
|
||||||
|
config_file_process(include_filename, buffer, included_files, test);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Normal line, add to buffer
|
||||||
|
g_string_append(buffer, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_file_process(const char *filename, GString *buffer, GList **included_files, bool test)
|
||||||
|
{
|
||||||
|
if (test)
|
||||||
|
{
|
||||||
|
// Test that configuration file can be parsed
|
||||||
|
|
||||||
|
GError *error = NULL;
|
||||||
|
GKeyFile *keyfile = g_key_file_new();
|
||||||
|
|
||||||
|
if (g_key_file_load_from_file(keyfile, filename, G_KEY_FILE_NONE, &error) == false)
|
||||||
|
{
|
||||||
|
tio_error_print("Failure loading file %s: %s", filename, error->message);
|
||||||
|
g_key_file_free(keyfile);
|
||||||
|
g_error_free(error);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *file = fopen(filename, "r");
|
||||||
|
if (file)
|
||||||
|
{
|
||||||
|
char line[MAX_LINE_LENGTH];
|
||||||
|
while (fgets(line, sizeof(line), file))
|
||||||
|
{
|
||||||
|
config_file_process_line(line, buffer, included_files, test);
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void config_file_load(const char *filename, GString *buffer, bool test)
|
||||||
|
{
|
||||||
|
char current_dir[PATH_MAX] = ".";
|
||||||
|
char *config_file_dir = dirname(strdup(config.path));
|
||||||
|
GList *included_files = NULL;
|
||||||
|
|
||||||
|
getcwd(current_dir, PATH_MAX);
|
||||||
|
|
||||||
|
// Change to the directory of the configuration file
|
||||||
|
chdir(config_file_dir);
|
||||||
|
|
||||||
|
config_file_process(filename, buffer, &included_files, test);
|
||||||
|
|
||||||
|
// Restore current directory
|
||||||
|
chdir(current_dir);
|
||||||
|
|
||||||
|
// Free memory
|
||||||
|
g_list_free_full(included_files, g_free);
|
||||||
|
free(config_file_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_file_parse(void)
|
||||||
|
{
|
||||||
|
// Find config file
|
||||||
|
if (config_file_resolve() != 0)
|
||||||
|
{
|
||||||
|
// None found - stop parsing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.target == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GString *config_buffer = g_string_new(NULL);
|
||||||
|
GKeyFile *keyfile = g_key_file_new();
|
||||||
|
GList *included_files = NULL;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
config_file_load(config.path, config_buffer, true);
|
||||||
|
|
||||||
|
if (g_key_file_load_from_data(keyfile, config_buffer->str, config_buffer->len, G_KEY_FILE_NONE, &error) == false)
|
||||||
|
{
|
||||||
|
tio_error_print("Failure loading file %s: %s", config.path, error->message);
|
||||||
|
g_string_free(config_buffer, TRUE);
|
||||||
|
g_key_file_free(keyfile);
|
||||||
|
g_error_free(error);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse default group/section
|
||||||
|
if (g_key_file_has_group(keyfile, CONFIG_GROUP_NAME_DEFAULT))
|
||||||
|
{
|
||||||
|
config_parse_keys(keyfile, CONFIG_GROUP_NAME_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse target
|
||||||
|
if (g_key_file_has_group(keyfile, option.target))
|
||||||
|
{
|
||||||
|
config.active_group = strdup(option.target);
|
||||||
|
config_parse_keys(keyfile, option.target);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Find group by pattern
|
||||||
|
gsize num_groups;
|
||||||
|
gchar **group = g_key_file_get_groups(keyfile, &num_groups);
|
||||||
|
|
||||||
|
for (gsize i = 0; i < num_groups; i++)
|
||||||
|
{
|
||||||
|
// Skip default group
|
||||||
|
if (strcmp(group[i], CONFIG_GROUP_NAME_DEFAULT) == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup 'pattern' key
|
||||||
|
gchar *pattern = g_key_file_get_string(keyfile, group[i], "pattern", &error);
|
||||||
|
if (error != NULL)
|
||||||
|
{
|
||||||
|
g_error_free(error);
|
||||||
|
error = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup 'device' key
|
||||||
|
gchar *device = g_key_file_get_string(keyfile, group[i], "device", &error);
|
||||||
|
if (error != NULL)
|
||||||
|
{
|
||||||
|
g_error_free(error);
|
||||||
|
error = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match pattern against target and replace any sub expression
|
||||||
|
// matches (%mN) in device string and return resulting string
|
||||||
|
// representing the new pattern based string.
|
||||||
|
config.device = match_and_replace(option.target, pattern, device);
|
||||||
|
if (config.device != NULL)
|
||||||
|
{
|
||||||
|
// Match found - save device
|
||||||
|
device = strdup(config.device);
|
||||||
|
|
||||||
|
// Parse found group (may replace config.device)
|
||||||
|
config_parse_keys(keyfile, group[i]);
|
||||||
|
|
||||||
|
// Update configuration
|
||||||
|
config.active_group = strdup(group[i]);
|
||||||
|
config.device = device; // Restore new device string
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
g_key_file_free(keyfile);
|
||||||
|
g_string_free(config_buffer, TRUE);
|
||||||
|
g_list_free_full(included_files, g_free);
|
||||||
|
|
||||||
|
atexit(&config_exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_exit(void)
|
||||||
|
{
|
||||||
|
free(config.active_group);
|
||||||
|
free(config.path);
|
||||||
|
free(config.device);
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_file_print(void)
|
||||||
|
{
|
||||||
|
if (config.path != NULL)
|
||||||
|
{
|
||||||
|
tio_printf(" Active configuration file: %s", config.path);
|
||||||
|
if (config.active_group != NULL)
|
||||||
|
{
|
||||||
|
tio_printf(" Active configuration profile: %s", config.active_group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void config_list_targets(void)
|
||||||
|
{
|
||||||
|
memset(&config, 0, sizeof(struct config_t));
|
||||||
|
|
||||||
|
// Find config file
|
||||||
|
if (config_file_resolve() != 0)
|
||||||
|
{
|
||||||
|
// None found
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GKeyFile *keyfile;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
keyfile = g_key_file_new();
|
||||||
|
|
||||||
|
GString *config_buffer = g_string_new(NULL);
|
||||||
|
|
||||||
|
config_file_load(config.path, config_buffer, false);
|
||||||
|
|
||||||
|
if (g_key_file_load_from_data(keyfile, config_buffer->str, config_buffer->len, G_KEY_FILE_NONE, &error) == false)
|
||||||
|
{
|
||||||
|
g_error_free(error);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all group names
|
||||||
|
gsize num_groups;
|
||||||
|
gchar **group = g_key_file_get_groups(keyfile, &num_groups);
|
||||||
|
|
||||||
|
if (num_groups == 0)
|
||||||
|
{
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nConfiguration profiles (%s)\n", config.path);
|
||||||
|
printf("--------------------------------------------------------------------------------\n");
|
||||||
|
|
||||||
|
int j = 1;
|
||||||
|
for (gsize i = 0; i < num_groups; i++)
|
||||||
|
{
|
||||||
|
// Skip default group
|
||||||
|
if (strcmp(group[i], CONFIG_GROUP_NAME_DEFAULT) == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip group with include directive
|
||||||
|
if (strncmp(group[i], CONFIG_GROUP_INCLUDE_PREFIX, strlen(CONFIG_GROUP_INCLUDE_PREFIX)) == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%-19s ", group[i]);
|
||||||
|
if (j++ % 4 == 0)
|
||||||
|
{
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((j-1) % 4 != 0)
|
||||||
|
{
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev(group);
|
||||||
|
cleanup:
|
||||||
|
g_key_file_free(keyfile);
|
||||||
|
g_string_free(config_buffer, TRUE);
|
||||||
|
}
|
||||||
38
src/configfile.h
Normal file
38
src/configfile.h
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Liam Beguin
|
||||||
|
* Copyright (c) 2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct config_t
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
char *active_group;
|
||||||
|
char *device;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct config_t config;
|
||||||
|
|
||||||
|
void config_file_print(void);
|
||||||
|
void config_file_parse(void);
|
||||||
|
void config_exit(void);
|
||||||
|
void config_file_show_profiles(void);
|
||||||
|
void config_list_targets(void);
|
||||||
86
src/error.c
86
src/error.c
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* tio - a simple TTY terminal I/O application
|
* tio - a serial device I/O tool
|
||||||
*
|
*
|
||||||
* Copyright (c) 2014-2017 Martin Lund
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -19,23 +19,79 @@
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#define _GNU_SOURCE // To access vasprintf
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include "print.h"
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include "tio/options.h"
|
|
||||||
#include "tio/print.h"
|
|
||||||
#include "tio/error.h"
|
|
||||||
|
|
||||||
char error[2][1000];
|
static char error[2][1000];
|
||||||
|
static bool in_session = false;
|
||||||
|
bool error_normal = true;
|
||||||
|
|
||||||
|
void switch_error_output_mode(void)
|
||||||
|
{
|
||||||
|
error_normal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_enter_session_mode(void)
|
||||||
|
{
|
||||||
|
in_session = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_printf_(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
vasprintf(&line, format, args);
|
||||||
|
|
||||||
|
if (in_session)
|
||||||
|
{
|
||||||
|
if (print_tainted)
|
||||||
|
{
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
ansi_error_printf("[%s] %s", timestamp_current_time(), line);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s\n", line);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
print_tainted = false;
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tio_error_printf(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
vsnprintf(error[0], 1000, format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tio_error_printf_silent(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
vsnprintf(error[1], 1000, format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
void error_exit(void)
|
void error_exit(void)
|
||||||
{
|
{
|
||||||
/* Always print errors but only print silent errors when in no autoconnect
|
|
||||||
* mode */
|
|
||||||
if (error[0][0] != 0)
|
if (error[0][0] != 0)
|
||||||
printf("\rError: %s\r\n", error[0]);
|
{
|
||||||
else if ((error[1][0] != 0) && (option.no_autoconnect))
|
/* Print error */
|
||||||
printf("\rError: %s\r\n", error[1]);
|
error_printf_("Error: %s", error[0]);
|
||||||
|
}
|
||||||
|
else if ((error[1][0] != 0) && (option.no_reconnect))
|
||||||
|
{
|
||||||
|
/* Print silent error */
|
||||||
|
error_printf_("Error: %s", error[1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* tio - a simple TTY terminal I/O application
|
* tio - a serial device I/O tool
|
||||||
*
|
*
|
||||||
* Copyright (c) 2014-2017 Martin Lund
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -19,20 +19,17 @@
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ERROR_H
|
#pragma once
|
||||||
#define ERROR_H
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
extern bool error_normal;
|
||||||
|
|
||||||
#define TIO_SUCCESS 0
|
#define TIO_SUCCESS 0
|
||||||
#define TIO_ERROR 1
|
#define TIO_ERROR 1
|
||||||
|
|
||||||
extern char error[2][1000];
|
void tio_error_printf(const char *format, ...);
|
||||||
|
void tio_error_printf_silent(const char *format, ...);
|
||||||
#define error_printf(format, args...) \
|
|
||||||
snprintf (error[0], 1000, format, ## args);
|
|
||||||
|
|
||||||
#define error_printf_silent(format, args...) \
|
|
||||||
snprintf (error[1], 1000, format, ## args);
|
|
||||||
|
|
||||||
void error_exit(void);
|
void error_exit(void);
|
||||||
|
void error_enter_session_mode(void);
|
||||||
#endif
|
void switch_error_output_mode(void);
|
||||||
226
src/fs.c
Normal file
226
src/fs.c
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2024 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE // For statx()
|
||||||
|
#include "config.h"
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include "error.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "options.h"
|
||||||
|
|
||||||
|
bool fs_dir_exists(const char *path)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (stat(path, &st) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (!S_ISDIR(st.st_mode))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to read the content of a file but stripped of newline
|
||||||
|
ssize_t fs_read_file_stripped(char *buf, size_t bufsiz, const char *format, ...)
|
||||||
|
{
|
||||||
|
char filename[PATH_MAX];
|
||||||
|
int bytes_printed = 0;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
bytes_printed = vsnprintf(filename, sizeof(filename), format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if (bytes_printed < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *file = fopen(filename, "r");
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ssize_t length = fread(buf, 1, bufsiz - 1, file);
|
||||||
|
if (length == -1)
|
||||||
|
{
|
||||||
|
fclose(file);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip any newline
|
||||||
|
buf[strcspn(buf, "\n")] = 0;
|
||||||
|
buf[length] = '\0'; // Make sure to null-terminate the string
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fs_file_exists(const char *format, ...)
|
||||||
|
{
|
||||||
|
char filename[PATH_MAX];
|
||||||
|
int bytes_printed = 0;
|
||||||
|
struct stat st;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
bytes_printed = vsnprintf(filename, sizeof(filename), format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if (bytes_printed < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stat(filename, &st) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* fs_search_directory(const char *dir_path, const char *dirname)
|
||||||
|
{
|
||||||
|
struct dirent *entry;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
struct stat st;
|
||||||
|
DIR *dir;
|
||||||
|
|
||||||
|
if ((dir = opendir(dir_path)) == NULL)
|
||||||
|
{
|
||||||
|
// Error opening directory
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((entry = readdir(dir)) != NULL)
|
||||||
|
{
|
||||||
|
snprintf(path, PATH_MAX, "%s/%s", dir_path, entry->d_name);
|
||||||
|
|
||||||
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lstat(path, &st) == -1)
|
||||||
|
{
|
||||||
|
// Error getting directory status
|
||||||
|
closedir(dir);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISLNK(st.st_mode))
|
||||||
|
{
|
||||||
|
// Skip symbolic links
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISDIR(st.st_mode))
|
||||||
|
{
|
||||||
|
// If it's a directory, check if it's the one we're looking for
|
||||||
|
if (strcmp(entry->d_name, dirname) == 0)
|
||||||
|
{
|
||||||
|
char *result = strndup(path, PATH_MAX);
|
||||||
|
closedir(dir);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Recursively search within directories
|
||||||
|
char* result = fs_search_directory(path, dirname);
|
||||||
|
if (result != NULL)
|
||||||
|
{
|
||||||
|
closedir(dir);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__linux__) && defined(STATX_BTIME)
|
||||||
|
|
||||||
|
// Function to return creation time of file
|
||||||
|
double fs_get_creation_time(const char *path)
|
||||||
|
{
|
||||||
|
struct statx stx;
|
||||||
|
int fd = open(path, O_RDONLY);
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statx(fd, "", AT_EMPTY_PATH, STATX_ALL, &stx) != 0)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the file
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return stx.stx_btime.tv_sec + stx.stx_btime.tv_nsec / 1e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__APPLE__) || defined(__MACH__)
|
||||||
|
|
||||||
|
double fs_get_creation_time(const char *path)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (stat(path, &st) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return st.st_mtimespec.tv_sec + st.st_mtimespec.tv_nsec / 1e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
double fs_get_creation_time(const char *path)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (stat(path, &st) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (double) st.st_ctime;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* tio - a simple TTY terminal I/O application
|
* tio - a serial device I/O tool
|
||||||
*
|
*
|
||||||
* Copyright (c) 2014-2017 Martin Lund
|
* Copyright (c) 2014-2024 Martin Lund
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -19,31 +19,13 @@
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef OPTIONS_H
|
#pragma once
|
||||||
#define OPTIONS_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <limits.h>
|
#include <stdio.h>
|
||||||
#include <termios.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
|
|
||||||
/* Options */
|
bool fs_dir_exists(const char *path);
|
||||||
struct option_t
|
bool fs_file_exists(const char *format, ...);
|
||||||
{
|
char* fs_search_directory(const char *dir_path, const char *dirname);
|
||||||
const char *tty_device;
|
ssize_t fs_read_file_stripped(char *buf, size_t bufsiz, const char *format, ...);
|
||||||
unsigned int baudrate;
|
double fs_get_creation_time(const char *path);
|
||||||
int databits;
|
|
||||||
char *flow;
|
|
||||||
int stopbits;
|
|
||||||
char *parity;
|
|
||||||
int output_delay;
|
|
||||||
bool no_autoconnect;
|
|
||||||
bool log;
|
|
||||||
const char *log_filename;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern struct option_t option;
|
|
||||||
|
|
||||||
void parse_options(int argc, char *argv[]);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
* tio - a simple TTY terminal I/O application
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014-2017 Martin Lund
|
|
||||||
*
|
|
||||||
* This program 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PRINT_H
|
|
||||||
#define PRINT_H
|
|
||||||
|
|
||||||
#define ANSI_COLOR_GRAY "\x1b[1;30m"
|
|
||||||
#define ANSI_COLOR_RED "\x1b[1;31m"
|
|
||||||
#define ANSI_COLOR_GREEN "\x1b[1;32m"
|
|
||||||
#define ANSI_COLOR_YELLOW "\x1b[1;33m"
|
|
||||||
#define ANSI_COLOR_BLUE "\x1b[1;34m"
|
|
||||||
#define ANSI_COLOR_PINK "\x1b[1;35m"
|
|
||||||
#define ANSI_COLOR_CYAN "\x1b[1;36m"
|
|
||||||
#define ANSI_COLOR_WHITE "\x1b[1;37m"
|
|
||||||
#define ANSI_COLOR_RESET "\x1b[0m"
|
|
||||||
|
|
||||||
#define color_printf(format, args...) \
|
|
||||||
fprintf (stdout, "\r" ANSI_COLOR_YELLOW format ANSI_COLOR_RESET "\r\n", ## args); \
|
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
#define warning_printf(format, args...) \
|
|
||||||
fprintf (stdout, "\rWarning: " format "\r\n", ## args); \
|
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
#define debug_printf(format, args...) \
|
|
||||||
fprintf (stdout, "[debug] " format, ## args)
|
|
||||||
#define debug_printf_raw(format, args...) \
|
|
||||||
fprintf (stdout, "" format, ## args)
|
|
||||||
#else
|
|
||||||
#define debug_printf(format, args...)
|
|
||||||
#define debug_printf_raw(format, args...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
228
src/log.c
228
src/log.c
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* tio - a simple TTY terminal I/O application
|
* tio - a serial device I/O tool
|
||||||
*
|
*
|
||||||
* Copyright (c) 2014-2017 Martin Lund
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -19,46 +19,224 @@
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#define _GNU_SOURCE // To access vasprintf
|
||||||
#include <stdio.h>
|
#include <sys/time.h>
|
||||||
#include <stdlib.h>
|
#include <libgen.h>
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include "tio/options.h"
|
#include "print.h"
|
||||||
#include "tio/print.h"
|
#include "fs.h"
|
||||||
#include "tio/error.h"
|
|
||||||
|
|
||||||
static FILE *fp;
|
#define IS_ESC_CSI_INTERMEDIATE_CHAR(c) ((c >= 0x20) && (c <= 0x3F))
|
||||||
static bool log_error = false;
|
#define IS_ESC_END_CHAR(c) ((c >= 0x30) && (c <= 0x7E))
|
||||||
|
#define IS_CTRL_CHAR(c) ((c >= 0x00) && (c <= 0x1F))
|
||||||
|
|
||||||
void log_open(const char *filename)
|
static FILE *fp = NULL;
|
||||||
|
static char file_buffer[BUFSIZ];
|
||||||
|
static const char *log_filename = NULL;
|
||||||
|
|
||||||
|
static char *date_time(void)
|
||||||
{
|
{
|
||||||
fp = fopen(filename, "w+");
|
static char date_time_string[50];
|
||||||
|
struct tm *tm;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
if (fp == NULL)
|
gettimeofday(&tv, NULL);
|
||||||
{
|
|
||||||
log_error = true;
|
tm = localtime(&tv.tv_sec);
|
||||||
exit(EXIT_FAILURE);
|
strftime(date_time_string, sizeof(date_time_string), "%Y-%m-%dT%H:%M:%S", tm);
|
||||||
}
|
|
||||||
|
return date_time_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_write(char c)
|
int log_open(const char *filename)
|
||||||
{
|
{
|
||||||
if (fp != NULL)
|
char *automatic_filename;
|
||||||
fputc(c, fp);
|
char *dir_plus_automatic_filename;
|
||||||
|
|
||||||
|
if (filename == NULL)
|
||||||
|
{
|
||||||
|
// Generate filename if none provided
|
||||||
|
if (option.auto_connect == AUTO_CONNECT_DIRECT)
|
||||||
|
{
|
||||||
|
// File name format ("tio_TARGET_YYYY-MM-DDTHH:MM:SS.log")
|
||||||
|
asprintf(&automatic_filename, "tio_%s_%s.log", basename((char *)option.target), date_time());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If using 'new' or 'latest' autoconnect strategy we simply use strategy
|
||||||
|
// name to name autogenerated log name as device names may vary
|
||||||
|
asprintf(&automatic_filename, "tio_%s_%s.log",
|
||||||
|
option_auto_connect_state_to_string(option.auto_connect),
|
||||||
|
date_time());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.log_directory != NULL)
|
||||||
|
{
|
||||||
|
if (fs_dir_exists(option.log_directory) == false)
|
||||||
|
{
|
||||||
|
tio_error_printf("Log directory not found");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
asprintf(&dir_plus_automatic_filename, "%s/%s", option.log_directory, automatic_filename);
|
||||||
|
filename = dir_plus_automatic_filename;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filename = automatic_filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_filename = filename;
|
||||||
|
|
||||||
|
// Open log file
|
||||||
|
if (option.log_append)
|
||||||
|
{
|
||||||
|
// Append to existing log file
|
||||||
|
fp = fopen(filename, "a+");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Truncate existing log file
|
||||||
|
fp = fopen(filename, "w+");
|
||||||
|
}
|
||||||
|
if (fp == NULL)
|
||||||
|
{
|
||||||
|
tio_warning_printf("Could not open log file %s (%s)", filename, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable line buffering
|
||||||
|
setvbuf(fp, file_buffer, _IOLBF, BUFSIZ);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool log_strip(char c)
|
||||||
|
{
|
||||||
|
static char previous_char = 0;
|
||||||
|
static bool esc_sequence = false;
|
||||||
|
bool strip = false;
|
||||||
|
|
||||||
|
/* Detect if character should be stripped or not */
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 0xa:
|
||||||
|
/* Line feed / new line */
|
||||||
|
/* Reset ESC sequence just in case something went wrong with the
|
||||||
|
* escape sequence parsing. */
|
||||||
|
esc_sequence = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x1b:
|
||||||
|
/* Escape */
|
||||||
|
strip = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x5b:
|
||||||
|
/* Left bracket */
|
||||||
|
if (previous_char == 0x1b)
|
||||||
|
{
|
||||||
|
// Start of ESC sequence
|
||||||
|
esc_sequence = true;
|
||||||
|
strip = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (IS_CTRL_CHAR(c))
|
||||||
|
{
|
||||||
|
/* Strip ASCII control characters */
|
||||||
|
strip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if ((esc_sequence) && (IS_ESC_CSI_INTERMEDIATE_CHAR(c)))
|
||||||
|
{
|
||||||
|
strip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if ((esc_sequence) && (IS_ESC_END_CHAR(c)))
|
||||||
|
{
|
||||||
|
esc_sequence = false;
|
||||||
|
strip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
previous_char = c;
|
||||||
|
|
||||||
|
return strip;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_printf(const char *format, ...)
|
||||||
|
{
|
||||||
|
if (fp == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
va_list(args);
|
||||||
|
va_start(args, format);
|
||||||
|
vasprintf(&line, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
fwrite(line, strlen(line), 1, fp);
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_putc(char c)
|
||||||
|
{
|
||||||
|
if (fp == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.output_mode == OUTPUT_MODE_HEX)
|
||||||
|
{
|
||||||
|
fprintf(fp, "%02x ", (unsigned char) c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (option.log_strip)
|
||||||
|
{
|
||||||
|
if (log_strip(c) == false)
|
||||||
|
{
|
||||||
|
fputc(c, fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fputc(c, fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_close(void)
|
void log_close(void)
|
||||||
{
|
{
|
||||||
if (fp != NULL)
|
if (fp != NULL)
|
||||||
|
{
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
tio_printf("Saved log to file %s", log_filename);
|
||||||
|
fp = NULL;
|
||||||
|
log_filename = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_exit(void)
|
void log_exit(void)
|
||||||
{
|
{
|
||||||
if (option.log)
|
if ((option.log) && (log_filename != NULL))
|
||||||
|
{
|
||||||
log_close();
|
log_close();
|
||||||
|
}
|
||||||
if (log_error)
|
}
|
||||||
error_printf("Could not open log file %s (%s)", option.log_filename, strerror(errno));
|
|
||||||
|
const char *log_get_filename(void)
|
||||||
|
{
|
||||||
|
return log_filename;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* tio - a simple TTY terminal I/O application
|
* tio - a serial device I/O tool
|
||||||
*
|
*
|
||||||
* Copyright (c) 2014-2017 Martin Lund
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -19,12 +19,11 @@
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LOG_H
|
#pragma once
|
||||||
#define LOG_H
|
|
||||||
|
|
||||||
void log_open(const char *filename);
|
int log_open(const char *filename);
|
||||||
void log_write(char c);
|
void log_printf(const char *format, ...);
|
||||||
|
void log_putc(char c);
|
||||||
void log_close(void);
|
void log_close(void);
|
||||||
void log_exit(void);
|
void log_exit(void);
|
||||||
|
const char * log_get_filename(void);
|
||||||
#endif
|
|
||||||
113
src/main.c
113
src/main.c
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* tio - a simple TTY terminal I/O application
|
* tio - a serial device I/O tool
|
||||||
*
|
*
|
||||||
* Copyright (c) 2014-2017 Martin Lund
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -19,49 +19,128 @@
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "tio/options.h"
|
#include "version.h"
|
||||||
#include "tio/tty.h"
|
#include "config.h"
|
||||||
#include "tio/log.h"
|
#include "options.h"
|
||||||
#include "tio/error.h"
|
#include "configfile.h"
|
||||||
#include "tio/print.h"
|
#include "tty.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "signals.h"
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int status;
|
int status = 0;
|
||||||
|
|
||||||
/* Install error exit handler */
|
/* Handle received signals */
|
||||||
|
signal_handlers_install();
|
||||||
|
|
||||||
|
/* Add error exit handler */
|
||||||
atexit(&error_exit);
|
atexit(&error_exit);
|
||||||
|
|
||||||
/* Parse options */
|
/* Parse command-line options (1st pass) */
|
||||||
parse_options(argc, argv);
|
options_parse(argc, argv);
|
||||||
|
|
||||||
|
if (option.complete_profiles)
|
||||||
|
{
|
||||||
|
config_file_show_profiles();
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse configuration file */
|
||||||
|
config_file_parse();
|
||||||
|
|
||||||
|
/* Parse command-line options (2nd pass) */
|
||||||
|
options_parse_final(argc, argv);
|
||||||
|
|
||||||
/* Configure tty device */
|
/* Configure tty device */
|
||||||
tty_configure();
|
tty_configure();
|
||||||
|
|
||||||
/* Configure output terminal */
|
/* Disable line buffering in stdout. This is necessary if we
|
||||||
stdout_configure();
|
* want things like local echo to work correctly. */
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
|
|
||||||
/* Install log exit handler */
|
/* Configure input terminal */
|
||||||
|
if (isatty(fileno(stdin)))
|
||||||
|
{
|
||||||
|
stdin_configure();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Enter non interactive mode
|
||||||
|
interactive_mode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Switch error output format */
|
||||||
|
switch_error_output_mode();
|
||||||
|
|
||||||
|
/* Configure output terminal */
|
||||||
|
if (isatty(fileno(stdout)))
|
||||||
|
{
|
||||||
|
stdout_configure();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No color when piping
|
||||||
|
option.color = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add log exit handler */
|
||||||
atexit(&log_exit);
|
atexit(&log_exit);
|
||||||
|
|
||||||
/* Create log file */
|
/* Create log file */
|
||||||
if (option.log)
|
if (option.log)
|
||||||
|
{
|
||||||
log_open(option.log_filename);
|
log_open(option.log_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize ANSI text formatting (colors etc.) */
|
||||||
|
print_init_ansi_formatting();
|
||||||
|
|
||||||
|
/* Change error printing mode */
|
||||||
|
error_enter_session_mode();
|
||||||
|
|
||||||
|
/* Print launch hints */
|
||||||
|
tio_printf("tio %s", VERSION);
|
||||||
|
if (interactive_mode)
|
||||||
|
{
|
||||||
|
tio_printf("Press ctrl-%c q to quit", option.prefix_key);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
tio_printf("Non-interactive mode enabled");
|
||||||
|
tio_printf("Press ctrl-c to quit");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open socket */
|
||||||
|
if (option.socket)
|
||||||
|
{
|
||||||
|
socket_configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Spawn input handling into separate thread */
|
||||||
|
tty_input_thread_create();
|
||||||
|
|
||||||
|
/* Wait for input to be ready */
|
||||||
|
tty_input_thread_wait_ready();
|
||||||
|
|
||||||
/* Connect to tty device */
|
/* Connect to tty device */
|
||||||
if (option.no_autoconnect)
|
if (option.no_reconnect)
|
||||||
|
{
|
||||||
|
tty_search();
|
||||||
status = tty_connect();
|
status = tty_connect();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Enter connect loop */
|
/* Enter connect loop */
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
tty_wait_for_device();
|
tty_wait_for_device();
|
||||||
status = tty_connect();
|
tty_connect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
76
src/meson.build
Normal file
76
src/meson.build
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
# Generate version header
|
||||||
|
version_h = vcs_tag(command : ['git', 'describe', '--tags', '--always', '--dirty'],
|
||||||
|
input : 'version.h.in',
|
||||||
|
output :'version.h',
|
||||||
|
replace_string:'@VERSION@')
|
||||||
|
|
||||||
|
config_h = configuration_data()
|
||||||
|
config_h.set('BAUDRATE_CASES', baudrate_cases)
|
||||||
|
configure_file(output: 'config.h', configuration: config_h)
|
||||||
|
|
||||||
|
tio_sources = [
|
||||||
|
'error.c',
|
||||||
|
'log.c',
|
||||||
|
'main.c',
|
||||||
|
'options.c',
|
||||||
|
'misc.c',
|
||||||
|
'tty.c',
|
||||||
|
'print.c',
|
||||||
|
'configfile.c',
|
||||||
|
'signals.c',
|
||||||
|
'socket.c',
|
||||||
|
'setspeed.c',
|
||||||
|
'rs485.c',
|
||||||
|
'timestamp.c',
|
||||||
|
'alert.c',
|
||||||
|
'xymodem.c',
|
||||||
|
'script.c',
|
||||||
|
'fs.c',
|
||||||
|
'readline.c',
|
||||||
|
version_h
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
foreach name: ['lua-5.4', 'lua-5.3', 'lua-5.2', 'lua-5.1', 'lua']
|
||||||
|
lua_dep = dependency(name, version: '>=5.1', required: false)
|
||||||
|
if lua_dep.found()
|
||||||
|
break
|
||||||
|
endif
|
||||||
|
endforeach
|
||||||
|
if not lua_dep.found()
|
||||||
|
error('Lua could not be found!')
|
||||||
|
endif
|
||||||
|
|
||||||
|
tio_dep = [
|
||||||
|
dependency('threads', required: true),
|
||||||
|
dependency('glib-2.0', required: true),
|
||||||
|
lua_dep
|
||||||
|
]
|
||||||
|
|
||||||
|
if host_machine.system() == 'darwin'
|
||||||
|
iokit_dep = dependency('appleframeworks', modules: ['IOKit'], required: true)
|
||||||
|
corefoundation_dep = dependency('appleframeworks', modules: ['CoreFoundation'], required: true)
|
||||||
|
tio_dep += [iokit_dep, corefoundation_dep]
|
||||||
|
endif
|
||||||
|
|
||||||
|
tio_c_args = ['-Wshadow','-Wno-unused-result']
|
||||||
|
|
||||||
|
if enable_setspeed2
|
||||||
|
tio_c_args += '-DHAVE_TERMIOS2'
|
||||||
|
endif
|
||||||
|
|
||||||
|
if enable_iossiospeed
|
||||||
|
tio_c_args += '-DHAVE_IOSSIOSPEED'
|
||||||
|
endif
|
||||||
|
|
||||||
|
if enable_rs485
|
||||||
|
tio_c_args += '-DHAVE_RS485'
|
||||||
|
endif
|
||||||
|
|
||||||
|
executable('tio',
|
||||||
|
tio_sources,
|
||||||
|
c_args: tio_c_args,
|
||||||
|
dependencies: tio_dep,
|
||||||
|
install: true )
|
||||||
|
|
||||||
|
subdir('bash-completion')
|
||||||
253
src/misc.c
Normal file
253
src/misc.c
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE // For FNM_EXTMATCH
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <fnmatch.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "print.h"
|
||||||
|
|
||||||
|
void delay(long ms)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
|
||||||
|
if (ms <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ts.tv_sec = ms / 1000;
|
||||||
|
ts.tv_nsec = (ms % 1000) * 1000000;
|
||||||
|
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ctrl_key_code(unsigned char key)
|
||||||
|
{
|
||||||
|
if ((key >= 'a') && (key <= 'z'))
|
||||||
|
{
|
||||||
|
return key & ~0x60;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool regex_match(const char *string, const char *pattern)
|
||||||
|
{
|
||||||
|
regex_t regex;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB) != 0)
|
||||||
|
{
|
||||||
|
// No match
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = regexec(®ex, string, (size_t) 0, NULL, 0);
|
||||||
|
regfree(®ex);
|
||||||
|
|
||||||
|
if (status != 0)
|
||||||
|
{
|
||||||
|
// No match
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_poll(int fd, void *data, size_t len, int timeout)
|
||||||
|
{
|
||||||
|
struct pollfd fds;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
fds.events = POLLIN;
|
||||||
|
fds.fd = fd;
|
||||||
|
|
||||||
|
/* Wait data available */
|
||||||
|
ret = poll(&fds, 1, timeout);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
tio_error_print("%s", strerror(errno));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else if (ret > 0)
|
||||||
|
{
|
||||||
|
if (fds.revents & POLLIN)
|
||||||
|
{
|
||||||
|
// Read ready data
|
||||||
|
return read(fd, data, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Timeout */
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to calculate djb2 hash of string
|
||||||
|
unsigned long djb2_hash(const unsigned char *str)
|
||||||
|
{
|
||||||
|
unsigned long hash = 5381;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
while ((c = *str++))
|
||||||
|
{
|
||||||
|
hash = ((hash << 5) + hash) + c; // hash * 33 + c
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to encode a number to base62
|
||||||
|
void *base62_encode(unsigned long num, char *output)
|
||||||
|
{
|
||||||
|
const char base62_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
if (output == NULL)
|
||||||
|
{
|
||||||
|
tio_error_print("Memory allocation failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
output[i] = base62_chars[num % 62];
|
||||||
|
num /= 62;
|
||||||
|
}
|
||||||
|
output[4] = '\0';
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to return current time
|
||||||
|
double get_current_time(void)
|
||||||
|
{
|
||||||
|
struct timespec current_time_ts;
|
||||||
|
|
||||||
|
if (clock_gettime(CLOCK_REALTIME, ¤t_time_ts) == -1)
|
||||||
|
{
|
||||||
|
// Error
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return current_time_ts.tv_sec + current_time_ts.tv_nsec / 1e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match_patterns(const char *string, const char *patterns)
|
||||||
|
{
|
||||||
|
char *pattern;
|
||||||
|
char *patterns_copy;
|
||||||
|
|
||||||
|
if ((string == NULL) || (patterns == NULL))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
patterns_copy = strdup(patterns);
|
||||||
|
|
||||||
|
// Tokenize the patterns string using strtok
|
||||||
|
pattern = strtok(patterns_copy, ",");
|
||||||
|
while (pattern != NULL)
|
||||||
|
{
|
||||||
|
// Check if the string matches the current pattern
|
||||||
|
#ifdef FNM_EXTMATCH
|
||||||
|
if (fnmatch(pattern, string, FNM_EXTMATCH) == 0)
|
||||||
|
#else
|
||||||
|
if (fnmatch(pattern, string, 0) == 0)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
free(patterns_copy);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next pattern
|
||||||
|
pattern = strtok(NULL, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(patterns_copy);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function that forks subprocess, redirects its stdin and stdout to the
|
||||||
|
// specified filedescriptor, and runs command.
|
||||||
|
int execute_shell_command(int fd, const char *command)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
// Fork a child process
|
||||||
|
pid = fork();
|
||||||
|
if (pid == -1)
|
||||||
|
{
|
||||||
|
// Error occurred
|
||||||
|
tio_error_print("fork() failed (%s)", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
else if (pid == 0)
|
||||||
|
{
|
||||||
|
// Child process
|
||||||
|
|
||||||
|
tio_printf("Executing shell command '%s'", command);
|
||||||
|
|
||||||
|
// Redirect stdout and stderr to the file descriptor
|
||||||
|
if (dup2(fd, STDOUT_FILENO) == -1 || dup2(fd, STDERR_FILENO) == -1)
|
||||||
|
{
|
||||||
|
tio_error_print("dup2() failed (%s)", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the shell command
|
||||||
|
execl("/bin/sh", "sh", "-c", command, (char *)NULL);
|
||||||
|
|
||||||
|
// If execlp() returns, it means an error occurred
|
||||||
|
perror("execlp");
|
||||||
|
tio_error_print("execlp() failed (%s)", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Parent process
|
||||||
|
|
||||||
|
// Wait for the child process to finish
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
|
||||||
|
if (WIFEXITED(status))
|
||||||
|
{
|
||||||
|
tio_printf("Command exited with status %d", WEXITSTATUS(status));
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tio_error_printf("Child process exited abnormally\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_line()
|
||||||
|
{
|
||||||
|
print("\r\033[K");
|
||||||
|
}
|
||||||
38
src/misc.h
Normal file
38
src/misc.h
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define UNUSED(expr) do { (void)(expr); } while (0)
|
||||||
|
|
||||||
|
void delay(long ms);
|
||||||
|
int ctrl_key_code(unsigned char key);
|
||||||
|
bool regex_match(const char *string, const char *pattern);
|
||||||
|
unsigned long djb2_hash(const unsigned char *str);
|
||||||
|
void *base62_encode(unsigned long num, char *output);
|
||||||
|
int read_poll(int fd, void *data, size_t len, int timeout);
|
||||||
|
double get_current_time(void);
|
||||||
|
bool match_patterns(const char *string, const char *patterns);
|
||||||
|
int execute_shell_command(int fd, const char *command);
|
||||||
|
void clear_line();
|
||||||
1136
src/options.c
1136
src/options.c
File diff suppressed because it is too large
Load diff
135
src/options.h
Normal file
135
src/options.h
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "script.h"
|
||||||
|
#include "timestamp.h"
|
||||||
|
#include "alert.h"
|
||||||
|
#include "tty.h"
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
INPUT_MODE_NORMAL,
|
||||||
|
INPUT_MODE_HEX,
|
||||||
|
INPUT_MODE_LINE,
|
||||||
|
INPUT_MODE_END,
|
||||||
|
} input_mode_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
OUTPUT_MODE_NORMAL,
|
||||||
|
OUTPUT_MODE_HEX,
|
||||||
|
OUTPUT_MODE_END,
|
||||||
|
} output_mode_t;
|
||||||
|
|
||||||
|
/* Options */
|
||||||
|
struct option_t
|
||||||
|
{
|
||||||
|
char *target;
|
||||||
|
int baudrate;
|
||||||
|
int databits;
|
||||||
|
flow_t flow;
|
||||||
|
int stopbits;
|
||||||
|
parity_t parity;
|
||||||
|
int output_delay;
|
||||||
|
int output_line_delay;
|
||||||
|
int dtr_pulse_duration;
|
||||||
|
int rts_pulse_duration;
|
||||||
|
int cts_pulse_duration;
|
||||||
|
int dsr_pulse_duration;
|
||||||
|
int dcd_pulse_duration;
|
||||||
|
int ri_pulse_duration;
|
||||||
|
bool no_reconnect;
|
||||||
|
auto_connect_t auto_connect;
|
||||||
|
bool log;
|
||||||
|
bool log_append;
|
||||||
|
bool log_strip;
|
||||||
|
bool local_echo;
|
||||||
|
timestamp_t timestamp;
|
||||||
|
char *log_filename;
|
||||||
|
char *log_directory;
|
||||||
|
char *socket;
|
||||||
|
int color;
|
||||||
|
input_mode_t input_mode;
|
||||||
|
output_mode_t output_mode;
|
||||||
|
char prefix_code;
|
||||||
|
char prefix_key;
|
||||||
|
bool prefix_enabled;
|
||||||
|
bool mute;
|
||||||
|
bool rs485;
|
||||||
|
uint32_t rs485_config_flags;
|
||||||
|
int32_t rs485_delay_rts_before_send;
|
||||||
|
int32_t rs485_delay_rts_after_send;
|
||||||
|
alert_t alert;
|
||||||
|
bool complete_profiles;
|
||||||
|
char *script;
|
||||||
|
char *script_filename;
|
||||||
|
script_run_t script_run;
|
||||||
|
int timestamp_timeout;
|
||||||
|
char *exclude_devices;
|
||||||
|
char *exclude_drivers;
|
||||||
|
char *exclude_tids;
|
||||||
|
int hex_n_value;
|
||||||
|
bool vt100;
|
||||||
|
char *exec;
|
||||||
|
bool map_i_nl_cr;
|
||||||
|
bool map_i_cr_nl;
|
||||||
|
bool map_ign_cr;
|
||||||
|
bool map_i_ff_escc;
|
||||||
|
bool map_i_nl_crnl;
|
||||||
|
bool map_i_cr_crnl;
|
||||||
|
bool map_o_cr_nl;
|
||||||
|
bool map_o_nl_crnl;
|
||||||
|
bool map_o_del_bs;
|
||||||
|
bool map_o_ltu;
|
||||||
|
bool map_o_nulbrk;
|
||||||
|
bool map_i_msb2lsb;
|
||||||
|
bool map_o_ign_cr;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct option_t option;
|
||||||
|
|
||||||
|
void options_print();
|
||||||
|
void options_parse(int argc, char *argv[]);
|
||||||
|
void options_parse_final(int argc, char *argv[]);
|
||||||
|
|
||||||
|
int option_string_to_integer(const char *string, int *value, const char *desc, int min, int max);
|
||||||
|
|
||||||
|
void option_parse_flow(const char *arg, flow_t *flow);
|
||||||
|
void option_parse_parity(const char *arg, parity_t *parity);
|
||||||
|
|
||||||
|
void option_parse_output_mode(const char *arg, output_mode_t *mode);
|
||||||
|
void option_parse_input_mode(const char *arg, input_mode_t *mode);
|
||||||
|
|
||||||
|
void option_parse_line_pulse_duration(const char *arg);
|
||||||
|
void option_parse_script_run(const char *arg, script_run_t *script_run);
|
||||||
|
void option_parse_alert(const char *arg, alert_t *alert);
|
||||||
|
|
||||||
|
void option_parse_auto_connect(const char *arg, auto_connect_t *auto_connect);
|
||||||
|
const char *option_auto_connect_state_to_string(auto_connect_t strategy);
|
||||||
|
|
||||||
|
void option_parse_timestamp(const char *arg, timestamp_t *timestamp);
|
||||||
|
const char* option_timestamp_format_to_string(timestamp_t timestamp);
|
||||||
|
|
||||||
|
void option_parse_mappings(const char *map);
|
||||||
112
src/print.c
Normal file
112
src/print.c
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "print.h"
|
||||||
|
|
||||||
|
bool print_tainted = false;
|
||||||
|
char ansi_format[30];
|
||||||
|
|
||||||
|
void print_hex(char c)
|
||||||
|
{
|
||||||
|
print_tainted = true;
|
||||||
|
printf("%02x ", (unsigned char) c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_normal(char c)
|
||||||
|
{
|
||||||
|
print_tainted = true;
|
||||||
|
putchar(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_init_ansi_formatting()
|
||||||
|
{
|
||||||
|
if (option.color == 256)
|
||||||
|
{
|
||||||
|
// Set bold text with no color changes
|
||||||
|
sprintf(ansi_format, "\e[1m");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Set bold text with user defined ANSI color
|
||||||
|
sprintf(ansi_format, "\e[1;38;5;%dm", option.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tio_printf_array(const char *array)
|
||||||
|
{
|
||||||
|
int i = 0, j = 0;
|
||||||
|
|
||||||
|
tio_printf("");
|
||||||
|
|
||||||
|
while (array[i])
|
||||||
|
{
|
||||||
|
if (array[i] == '\n')
|
||||||
|
{
|
||||||
|
const char *line = &array[j];
|
||||||
|
char *line_copy = strndup(line, i-j);
|
||||||
|
tio_printf_raw("%s\r", line_copy);
|
||||||
|
free(line_copy);
|
||||||
|
j = i;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
tio_printf("");
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_tainted_set()
|
||||||
|
{
|
||||||
|
print_tainted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
vprintf(format, args);
|
||||||
|
fflush(stdout);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
print_tainted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_padded(char *string, size_t length, char pad_char)
|
||||||
|
{
|
||||||
|
size_t padding = 0;
|
||||||
|
size_t string_length = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
string_length = strlen(string);
|
||||||
|
|
||||||
|
if (string_length < length)
|
||||||
|
{
|
||||||
|
padding += length - string_length;
|
||||||
|
printf("%s", string);
|
||||||
|
for (i=0; i<padding; i++)
|
||||||
|
{
|
||||||
|
putchar(pad_char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("%s", string);
|
||||||
|
}
|
||||||
|
}
|
||||||
143
src/print.h
Normal file
143
src/print.h
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "error.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "timestamp.h"
|
||||||
|
|
||||||
|
extern bool print_tainted;
|
||||||
|
extern char ansi_format[];
|
||||||
|
|
||||||
|
#define ANSI_RESET "\e[0m"
|
||||||
|
|
||||||
|
#define ansi_printf(format, args...) \
|
||||||
|
{ \
|
||||||
|
if (!option.mute) \
|
||||||
|
{ \
|
||||||
|
if (option.color < 0) \
|
||||||
|
fprintf (stdout, "\r" format "\r\n", ## args); \
|
||||||
|
else \
|
||||||
|
fprintf (stdout, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ansi_error_printf(format, args...) \
|
||||||
|
{ \
|
||||||
|
if (!option.mute) \
|
||||||
|
{ \
|
||||||
|
if (option.color < 0) \
|
||||||
|
fprintf (stderr, "\r" format "\r\n", ## args); \
|
||||||
|
else \
|
||||||
|
fprintf (stderr, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \
|
||||||
|
fflush(stderr); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ansi_printf_raw(format, args...) \
|
||||||
|
{ \
|
||||||
|
if (!option.mute) \
|
||||||
|
{ \
|
||||||
|
if (option.color < 0) \
|
||||||
|
fprintf (stdout, format, ## args); \
|
||||||
|
else \
|
||||||
|
fprintf (stdout, "%s" format ANSI_RESET, ansi_format, ## args); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define tio_warning_printf(format, args...) \
|
||||||
|
{ \
|
||||||
|
if (!option.mute) \
|
||||||
|
{ \
|
||||||
|
if (print_tainted) \
|
||||||
|
putchar('\n'); \
|
||||||
|
if (option.color < 0) \
|
||||||
|
fprintf (stdout, "\r[%s] Warning: " format "\r\n", timestamp_current_time(), ## args); \
|
||||||
|
else \
|
||||||
|
ansi_printf("[%s] Warning: " format, timestamp_current_time(), ## args); \
|
||||||
|
print_tainted = false; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define tio_error_print(format, args...) \
|
||||||
|
{ \
|
||||||
|
if (!option.mute) \
|
||||||
|
{ \
|
||||||
|
if (print_tainted) \
|
||||||
|
putchar('\n'); \
|
||||||
|
if (option.color < 0) { \
|
||||||
|
if (error_normal) \
|
||||||
|
fprintf (stderr, "Error: " format "\n", ## args); \
|
||||||
|
else \
|
||||||
|
fprintf (stderr, "\r[%s] Error: " format "\r\n", timestamp_current_time(), ## args); \
|
||||||
|
} \
|
||||||
|
else { \
|
||||||
|
if (error_normal) \
|
||||||
|
{ ansi_error_printf("Error: " format, ## args); }\
|
||||||
|
else \
|
||||||
|
{ ansi_error_printf("[%s] Error: " format, timestamp_current_time(), ## args); }\
|
||||||
|
} \
|
||||||
|
print_tainted = false; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define tio_printf(format, args...) \
|
||||||
|
{ \
|
||||||
|
if (!option.mute) \
|
||||||
|
{ \
|
||||||
|
if (print_tainted) \
|
||||||
|
putchar('\n'); \
|
||||||
|
ansi_printf("[%s] " format, timestamp_current_time(), ## args); \
|
||||||
|
print_tainted = false; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define tio_printf_raw(format, args...) \
|
||||||
|
{ \
|
||||||
|
if (!option.mute) \
|
||||||
|
{ \
|
||||||
|
if (print_tainted) \
|
||||||
|
putchar('\n'); \
|
||||||
|
ansi_printf_raw("[%s] " format, timestamp_current_time(), ## args); \
|
||||||
|
print_tainted = false; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define tio_debug_printf(format, args...) \
|
||||||
|
fprintf(stdout, "[debug] " format, ## args)
|
||||||
|
#define tio_debug_printf_raw(format, args...) \
|
||||||
|
fprintf(stdout, "" format, ## args)
|
||||||
|
#else
|
||||||
|
#define tio_debug_printf(format, args...)
|
||||||
|
#define tio_debug_printf_raw(format, args...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void print(const char *format, ...);
|
||||||
|
void print_hex(char c);
|
||||||
|
void print_normal(char c);
|
||||||
|
void print_init_ansi_formatting(void);
|
||||||
|
void tio_printf_array(const char *array);
|
||||||
|
void print_tainted_set(void);
|
||||||
|
void print_padded(char *string, size_t length, char pad_char);
|
||||||
276
src/readline.c
Normal file
276
src/readline.c
Normal file
|
|
@ -0,0 +1,276 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2024 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "print.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
#define RL_LINE_LENGTH_MAX PATH_MAX
|
||||||
|
#define RL_HISTORY_MAX 1000
|
||||||
|
|
||||||
|
static char rl_line[RL_LINE_LENGTH_MAX] = {};
|
||||||
|
static char *rl_history[RL_HISTORY_MAX];
|
||||||
|
static int rl_history_count = 0;
|
||||||
|
static int rl_history_index = 0;
|
||||||
|
static int rl_line_length = 0;
|
||||||
|
static int rl_cursor_pos = 0;
|
||||||
|
static int rl_escape = 0;
|
||||||
|
|
||||||
|
static void print_line(const char *string, int cursor_pos)
|
||||||
|
{
|
||||||
|
clear_line();
|
||||||
|
print("%s", string);
|
||||||
|
print("\r"); // Move the cursor back to the beginning
|
||||||
|
for (int i = 0; i < cursor_pos; ++i)
|
||||||
|
{
|
||||||
|
print("\x1b[C"); // Move the cursor right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void readline_init(void)
|
||||||
|
{
|
||||||
|
rl_history_count = 0;
|
||||||
|
rl_history_index = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < RL_HISTORY_MAX; ++i)
|
||||||
|
{
|
||||||
|
rl_history[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rl_line[0] = 0;
|
||||||
|
rl_line_length = 0;
|
||||||
|
rl_cursor_pos = 0;
|
||||||
|
rl_escape = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * readline_get(void)
|
||||||
|
{
|
||||||
|
return rl_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readline_input_char(char input_char)
|
||||||
|
{
|
||||||
|
if (rl_line_length < RL_LINE_LENGTH_MAX - 1)
|
||||||
|
{
|
||||||
|
memmove(&rl_line[rl_cursor_pos + 1], &rl_line[rl_cursor_pos], rl_line_length - rl_cursor_pos);
|
||||||
|
rl_line[rl_cursor_pos] = input_char;
|
||||||
|
rl_line_length++;
|
||||||
|
rl_cursor_pos++;
|
||||||
|
rl_line[rl_line_length] = '\0';
|
||||||
|
print_line(rl_line, rl_cursor_pos);
|
||||||
|
}
|
||||||
|
rl_escape = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readline_input_cr(void)
|
||||||
|
{
|
||||||
|
if (rl_line_length > 0)
|
||||||
|
{
|
||||||
|
// Save to history
|
||||||
|
if (rl_history_count < RL_HISTORY_MAX)
|
||||||
|
{
|
||||||
|
rl_history[rl_history_count] = strndup(rl_line, rl_line_length);
|
||||||
|
rl_history_count++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
free(rl_history[0]);
|
||||||
|
memmove(&rl_history[0], &rl_history[1], (RL_HISTORY_MAX - 1) * sizeof(char*));
|
||||||
|
rl_history[RL_HISTORY_MAX - 1] = strndup(rl_line, rl_line_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rl_line[rl_line_length] = '\0';
|
||||||
|
if (option.local_echo == false)
|
||||||
|
{
|
||||||
|
clear_line();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
print("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
rl_line_length = 0;
|
||||||
|
rl_cursor_pos = 0;
|
||||||
|
rl_history_index = rl_history_count;
|
||||||
|
rl_escape = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readline_input_bs(void)
|
||||||
|
{
|
||||||
|
if (rl_cursor_pos > 0)
|
||||||
|
{
|
||||||
|
memmove(&rl_line[rl_cursor_pos - 1], &rl_line[rl_cursor_pos], rl_line_length - rl_cursor_pos);
|
||||||
|
rl_line_length--;
|
||||||
|
rl_cursor_pos--;
|
||||||
|
rl_line[rl_line_length] = '\0';
|
||||||
|
print_line(rl_line, rl_cursor_pos);
|
||||||
|
}
|
||||||
|
rl_escape = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readline_input_escape(void)
|
||||||
|
{
|
||||||
|
rl_escape = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readline_input_left_bracket(void)
|
||||||
|
{
|
||||||
|
if (rl_escape == 1)
|
||||||
|
{
|
||||||
|
rl_escape = 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rl_escape = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readline_input_A(void)
|
||||||
|
{
|
||||||
|
if (rl_escape == 2)
|
||||||
|
{
|
||||||
|
// Up arrow
|
||||||
|
if (rl_history_index > 0)
|
||||||
|
{
|
||||||
|
rl_history_index--;
|
||||||
|
strncpy(rl_line, rl_history[rl_history_index], RL_LINE_LENGTH_MAX-1);
|
||||||
|
rl_line_length = strlen(rl_line);
|
||||||
|
rl_cursor_pos = rl_line_length;
|
||||||
|
print_line(rl_line, rl_cursor_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
readline_input_char('A');
|
||||||
|
}
|
||||||
|
|
||||||
|
rl_escape = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readline_input_B(void)
|
||||||
|
{
|
||||||
|
if (rl_escape == 2)
|
||||||
|
{
|
||||||
|
// Down arrow
|
||||||
|
if (rl_history_index < rl_history_count - 1)
|
||||||
|
{
|
||||||
|
rl_history_index++;
|
||||||
|
strncpy(rl_line, rl_history[rl_history_index], RL_LINE_LENGTH_MAX-1);
|
||||||
|
rl_line_length = strlen(rl_line);
|
||||||
|
rl_cursor_pos = rl_line_length;
|
||||||
|
print_line(rl_line, rl_cursor_pos);
|
||||||
|
}
|
||||||
|
else if (rl_history_index == rl_history_count - 1)
|
||||||
|
{
|
||||||
|
rl_history_index++;
|
||||||
|
rl_line_length = 0;
|
||||||
|
rl_cursor_pos = 0;
|
||||||
|
rl_line[rl_line_length] = '\0';
|
||||||
|
print_line(rl_line, rl_cursor_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
readline_input_char('B');
|
||||||
|
}
|
||||||
|
|
||||||
|
rl_escape = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readline_input_C(void)
|
||||||
|
{
|
||||||
|
if (rl_escape == 2)
|
||||||
|
{
|
||||||
|
// Right arrow
|
||||||
|
if (rl_cursor_pos < rl_line_length)
|
||||||
|
{
|
||||||
|
rl_cursor_pos++;
|
||||||
|
print("\x1b[C");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
readline_input_char('C');
|
||||||
|
}
|
||||||
|
|
||||||
|
rl_escape = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readline_input_D(void)
|
||||||
|
{
|
||||||
|
if (rl_escape == 2)
|
||||||
|
{
|
||||||
|
// Left arrow
|
||||||
|
if (rl_cursor_pos > 0)
|
||||||
|
{
|
||||||
|
rl_cursor_pos--;
|
||||||
|
print("\b");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
readline_input_char('D');
|
||||||
|
}
|
||||||
|
|
||||||
|
rl_escape = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void readline_input(char input_char)
|
||||||
|
{
|
||||||
|
switch (input_char)
|
||||||
|
{
|
||||||
|
case '\r': // Carriage return
|
||||||
|
readline_input_cr();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 127: // Backspace
|
||||||
|
readline_input_bs();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 27: // Escape
|
||||||
|
readline_input_escape();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '[':
|
||||||
|
readline_input_left_bracket();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'A':
|
||||||
|
readline_input_A();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'B':
|
||||||
|
readline_input_B();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'C':
|
||||||
|
readline_input_C();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'D':
|
||||||
|
readline_input_D();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
readline_input_char(input_char);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/readline.h
Normal file
26
src/readline.h
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2024 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
void readline_init(void);
|
||||||
|
void readline_input(char input_char);
|
||||||
|
char * readline_get(void);
|
||||||
210
src/rs485.c
Normal file
210
src/rs485.c
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include "options.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_RS485
|
||||||
|
|
||||||
|
#include <linux/serial.h>
|
||||||
|
|
||||||
|
static struct serial_rs485 rs485_config_saved;
|
||||||
|
static struct serial_rs485 rs485_config;
|
||||||
|
static bool rs485_config_written = false;
|
||||||
|
|
||||||
|
void rs485_parse_config(const char *arg)
|
||||||
|
{
|
||||||
|
bool token_found = true;
|
||||||
|
char *token = NULL;
|
||||||
|
char *buffer = strdup(arg);
|
||||||
|
|
||||||
|
while (token_found == true)
|
||||||
|
{
|
||||||
|
if (token == NULL)
|
||||||
|
{
|
||||||
|
token = strtok(buffer,",");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token = strtok(NULL, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token != NULL)
|
||||||
|
{
|
||||||
|
char keyname[31];
|
||||||
|
unsigned int value;
|
||||||
|
int match_count;
|
||||||
|
|
||||||
|
match_count = sscanf(token, "%30[^=]=%d", keyname, &value);
|
||||||
|
|
||||||
|
if (match_count == 2)
|
||||||
|
{
|
||||||
|
if (!strcmp(keyname, "RTS_ON_SEND"))
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
/* Set logical level for RTS pin equal to 1 when sending */
|
||||||
|
option.rs485_config_flags |= SER_RS485_RTS_ON_SEND;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Set logical level for RTS pin equal to 0 when sending */
|
||||||
|
option.rs485_config_flags &= ~(SER_RS485_RTS_ON_SEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!strcmp(keyname, "RTS_AFTER_SEND"))
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
/* Set logical level for RTS pin equal to 1 after sending */
|
||||||
|
option.rs485_config_flags |= SER_RS485_RTS_AFTER_SEND;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Set logical level for RTS pin equal to 0 after sending */
|
||||||
|
option.rs485_config_flags &= ~(SER_RS485_RTS_AFTER_SEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!strcmp(keyname, "RTS_DELAY_BEFORE_SEND"))
|
||||||
|
{
|
||||||
|
/* Set RTS delay before send */
|
||||||
|
option.rs485_delay_rts_before_send = value;
|
||||||
|
}
|
||||||
|
else if (!strcmp(keyname, "RTS_DELAY_AFTER_SEND"))
|
||||||
|
{
|
||||||
|
/* Set RTS delay after send */
|
||||||
|
option.rs485_delay_rts_after_send = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (match_count == 1)
|
||||||
|
{
|
||||||
|
if (!strcmp(keyname, "RX_DURING_TX"))
|
||||||
|
{
|
||||||
|
/* Receive data even while sending data */
|
||||||
|
option.rs485_config_flags |= SER_RS485_RX_DURING_TX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token_found = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token_found = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rs485_print_config(void)
|
||||||
|
{
|
||||||
|
tio_printf(" RS-485 Configuration:");
|
||||||
|
tio_printf(" RTS_ON_SEND: %s", (rs485_config.flags & SER_RS485_RTS_ON_SEND) ? "high" : "low");
|
||||||
|
tio_printf(" RTS_AFTER_SEND: %s", (rs485_config.flags & SER_RS485_RTS_AFTER_SEND) ? "high" : "low");
|
||||||
|
tio_printf(" RTS_DELAY_BEFORE_SEND = %d", rs485_config.delay_rts_before_send);
|
||||||
|
tio_printf(" RTS_DELAY_AFTER_SEND = %d", rs485_config.delay_rts_after_send);
|
||||||
|
tio_printf(" RX_DURING_TX: %s", (rs485_config.flags & SER_RS485_RX_DURING_TX) ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
int rs485_mode_enable(int fd)
|
||||||
|
{
|
||||||
|
/* Save existing RS-485 configuration */
|
||||||
|
ioctl (fd, TIOCGRS485, &rs485_config_saved);
|
||||||
|
|
||||||
|
/* Prepare new RS-485 configuration */
|
||||||
|
rs485_config.flags = SER_RS485_ENABLED;
|
||||||
|
rs485_config.flags |= option.rs485_config_flags;
|
||||||
|
|
||||||
|
if (option.rs485_delay_rts_before_send > 0)
|
||||||
|
{
|
||||||
|
rs485_config.delay_rts_before_send = option.rs485_delay_rts_before_send;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rs485_config.delay_rts_before_send = rs485_config_saved.delay_rts_before_send;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.rs485_delay_rts_after_send > 0)
|
||||||
|
{
|
||||||
|
rs485_config.delay_rts_after_send = option.rs485_delay_rts_after_send;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rs485_config.delay_rts_after_send = rs485_config_saved.delay_rts_after_send;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write new RS-485 configuration */
|
||||||
|
if (ioctl(fd, TIOCSRS485, &rs485_config) < 0)
|
||||||
|
{
|
||||||
|
tio_warning_printf("RS-485 mode is not supported by your device (%s)", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs485_config_written = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rs485_mode_restore(int fd)
|
||||||
|
{
|
||||||
|
if (rs485_config_written)
|
||||||
|
{
|
||||||
|
/* Write saved RS-485 configuration */
|
||||||
|
if (ioctl(fd, TIOCSRS485, &rs485_config_saved) < 0)
|
||||||
|
{
|
||||||
|
tio_warning_printf("TIOCGRS485 ioctl failed (%s)", strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void rs485_parse_config(const char *arg)
|
||||||
|
{
|
||||||
|
UNUSED(arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rs485_print_config(void)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rs485_mode_enable(int fd)
|
||||||
|
{
|
||||||
|
UNUSED(fd);
|
||||||
|
tio_error_printf("RS485 mode is not supported on your system");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rs485_mode_restore(int fd)
|
||||||
|
{
|
||||||
|
UNUSED(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
27
src/rs485.h
Normal file
27
src/rs485.h
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
void rs485_parse_config(const char *arg);
|
||||||
|
int rs485_mode_enable(int fd);
|
||||||
|
void rs485_mode_restore(int fd);
|
||||||
|
void rs485_print_config(void);
|
||||||
547
src/script.c
Normal file
547
src/script.c
Normal file
|
|
@ -0,0 +1,547 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2024 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
#include <lualib.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "misc.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "tty.h"
|
||||||
|
#include "xymodem.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "script.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "timestamp.h"
|
||||||
|
#include "termios.h"
|
||||||
|
|
||||||
|
#define MAX_BUFFER_SIZE 2000 // Maximum size of circular buffer
|
||||||
|
#define READ_LINE_SIZE 4096 // read_line buffer length
|
||||||
|
|
||||||
|
static int device_fd;
|
||||||
|
|
||||||
|
static char script_init[] =
|
||||||
|
"tio.set = function(arg)\n"
|
||||||
|
" local dtr = arg.DTR or -1\n"
|
||||||
|
" local rts = arg.RTS or -1\n"
|
||||||
|
" local cts = arg.CTS or -1\n"
|
||||||
|
" local dsr = arg.DSR or -1\n"
|
||||||
|
" local cd = arg.CD or -1\n"
|
||||||
|
" local ri = arg.RI or -1\n"
|
||||||
|
" tio.line_set(dtr, rts, cts, dsr, cd, ri)\n"
|
||||||
|
"end\n"
|
||||||
|
"tio.expect = function(pattern, timeout)\n"
|
||||||
|
" local str = ''\n"
|
||||||
|
" while true do\n"
|
||||||
|
" local c = tio.read(1, timeout)\n"
|
||||||
|
" if c then\n"
|
||||||
|
" str = str .. c\n"
|
||||||
|
" if string.match(str, pattern) then\n"
|
||||||
|
" return string.match(str, pattern)\n"
|
||||||
|
" end\n"
|
||||||
|
" else\n"
|
||||||
|
" return nil, str\n"
|
||||||
|
" end\n"
|
||||||
|
" end\n"
|
||||||
|
"end\n"
|
||||||
|
"tio.alwaysecho = true\n"
|
||||||
|
"setmetatable(tio, tio)\n";
|
||||||
|
|
||||||
|
static bool alwaysecho(lua_State *L)
|
||||||
|
{
|
||||||
|
bool b;
|
||||||
|
|
||||||
|
lua_getglobal(L, "tio");
|
||||||
|
lua_getfield(L, -1, "alwaysecho");
|
||||||
|
b = lua_toboolean(L, -1);
|
||||||
|
lua_pop(L, 2);
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int api_echo(lua_State *L)
|
||||||
|
{
|
||||||
|
size_t len = 0;
|
||||||
|
const char *str = luaL_checklstring(L, 1, &len);
|
||||||
|
|
||||||
|
if (option.timestamp)
|
||||||
|
{
|
||||||
|
char *pTimeStampNow = timestamp_current_time();
|
||||||
|
if (pTimeStampNow)
|
||||||
|
{
|
||||||
|
tio_printf("%s", str);
|
||||||
|
if (option.log)
|
||||||
|
{
|
||||||
|
log_printf("\n[%s] %s", pTimeStampNow, str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (size_t i=0; i<len; i++)
|
||||||
|
{
|
||||||
|
putchar(str[i]);
|
||||||
|
|
||||||
|
if (option.log)
|
||||||
|
log_putc(str[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void maybe_echo(lua_State *L)
|
||||||
|
{
|
||||||
|
if (alwaysecho(L))
|
||||||
|
{
|
||||||
|
lua_pushcfunction(L, api_echo);
|
||||||
|
lua_pushvalue(L, -2);
|
||||||
|
lua_call(L, 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lua: tio.sleep(seconds)
|
||||||
|
static int api_sleep(lua_State *L)
|
||||||
|
{
|
||||||
|
long seconds = lua_tointeger(L, 1);
|
||||||
|
|
||||||
|
if (seconds < 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tio_printf("Sleeping %ld seconds", seconds);
|
||||||
|
|
||||||
|
sleep(seconds);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lua: tio.msleep(miliseconds)
|
||||||
|
static int api_msleep(lua_State *L)
|
||||||
|
{
|
||||||
|
long mseconds = lua_tointeger(L, 1);
|
||||||
|
long useconds = mseconds * 1000;
|
||||||
|
|
||||||
|
if (useconds < 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tio_printf("Sleeping %ld ms", mseconds);
|
||||||
|
usleep(useconds);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lua: tio.line_set(dtr,rts,cts,dsr,cd,ri)
|
||||||
|
static int line_set(lua_State *L)
|
||||||
|
{
|
||||||
|
tty_line_config_t line_config[6] = { };
|
||||||
|
|
||||||
|
int dtr = lua_tointeger(L, 1);
|
||||||
|
int rts = lua_tointeger(L, 2);
|
||||||
|
int cts = lua_tointeger(L, 3);
|
||||||
|
int dsr = lua_tointeger(L, 4);
|
||||||
|
int cd = lua_tointeger(L, 5);
|
||||||
|
int ri = lua_tointeger(L, 6);
|
||||||
|
|
||||||
|
if (dtr != -1)
|
||||||
|
{
|
||||||
|
line_config[0].mask = TIOCM_DTR;
|
||||||
|
line_config[0].value = dtr;
|
||||||
|
line_config[0].reserved = true;
|
||||||
|
}
|
||||||
|
if (rts != -1)
|
||||||
|
{
|
||||||
|
line_config[1].mask = TIOCM_RTS;
|
||||||
|
line_config[1].value = rts;
|
||||||
|
line_config[1].reserved = true;
|
||||||
|
}
|
||||||
|
if (cts != -1)
|
||||||
|
{
|
||||||
|
line_config[2].mask = TIOCM_CTS;
|
||||||
|
line_config[2].value = cts;
|
||||||
|
line_config[2].reserved = true;
|
||||||
|
}
|
||||||
|
if (dsr != -1)
|
||||||
|
{
|
||||||
|
line_config[3].mask = TIOCM_DSR;
|
||||||
|
line_config[3].value = dsr;
|
||||||
|
line_config[3].reserved = true;
|
||||||
|
}
|
||||||
|
if (cd != -1)
|
||||||
|
{
|
||||||
|
line_config[4].mask = TIOCM_CD;
|
||||||
|
line_config[4].value = cd;
|
||||||
|
line_config[4].reserved = true;
|
||||||
|
}
|
||||||
|
if (ri != -1)
|
||||||
|
{
|
||||||
|
line_config[5].mask = TIOCM_RI;
|
||||||
|
line_config[5].value = ri;
|
||||||
|
line_config[5].reserved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
tty_line_set(device_fd, line_config);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lua: tio.send(file, protocol)
|
||||||
|
static int api_send(lua_State *L)
|
||||||
|
{
|
||||||
|
const char *file = luaL_checkstring(L, 1);
|
||||||
|
int protocol = luaL_checkinteger(L, 2);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (file == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (protocol)
|
||||||
|
{
|
||||||
|
case XMODEM_1K:
|
||||||
|
tio_printf("Sending file '%s' using XMODEM-1K", file);
|
||||||
|
ret = xymodem_send(device_fd, file, XMODEM_1K);
|
||||||
|
tio_printf("%s", ret < 0 ? "Aborted" : "Done");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case XMODEM_CRC:
|
||||||
|
tio_printf("Sending file '%s' using XMODEM-CRC", file);
|
||||||
|
ret = xymodem_send(device_fd, file, XMODEM_CRC);
|
||||||
|
tio_printf("%s", ret < 0 ? "Aborted" : "Done");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case YMODEM:
|
||||||
|
tio_printf("Sending file '%s' using YMODEM", file);
|
||||||
|
ret = xymodem_send(device_fd, file, YMODEM);
|
||||||
|
tio_printf("%s", ret < 0 ? "Aborted" : "Done");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lua: tio.write(string)
|
||||||
|
static int api_write(lua_State *L)
|
||||||
|
{
|
||||||
|
size_t len = 0;
|
||||||
|
const char *string = luaL_checklstring(L, 1, &len);
|
||||||
|
ssize_t ret;
|
||||||
|
int attempts = 100;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = write(device_fd, string, len);
|
||||||
|
if (ret < 0)
|
||||||
|
return luaL_error(L, "%s", strerror(errno));
|
||||||
|
|
||||||
|
len -= ret;
|
||||||
|
string += ret;
|
||||||
|
} while (len > 0 && --attempts);
|
||||||
|
|
||||||
|
if (len > 0)
|
||||||
|
return luaL_error(L, "partial write");
|
||||||
|
|
||||||
|
fsync(device_fd); // flush these characters now
|
||||||
|
tcdrain(device_fd); //ensure we flushed characters to our device
|
||||||
|
|
||||||
|
lua_getglobal(L, "tio");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lua: tio.read(size, timeout)
|
||||||
|
static int api_read(lua_State *L)
|
||||||
|
{
|
||||||
|
int size = luaL_checkinteger(L, 1);
|
||||||
|
int timeout = lua_tointeger(L, 2);
|
||||||
|
|
||||||
|
if (timeout == 0)
|
||||||
|
{
|
||||||
|
timeout = -1; // Wait forever
|
||||||
|
}
|
||||||
|
|
||||||
|
luaL_Buffer buffer;
|
||||||
|
luaL_buffinit(L, &buffer);
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM >= 502
|
||||||
|
char *p = luaL_prepbuffsize(&buffer, size);
|
||||||
|
#else
|
||||||
|
if (size > LUAL_BUFFERSIZE)
|
||||||
|
return luaL_error(L, "buffer overflow, max size is: %d", LUAL_BUFFERSIZE);
|
||||||
|
char *p = luaL_prepbuffer(&buffer);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ssize_t ret = read_poll(device_fd, p, size, timeout);
|
||||||
|
if (ret < 0)
|
||||||
|
return luaL_error(L, "%s", strerror(errno));
|
||||||
|
|
||||||
|
luaL_addsize(&buffer, ret);
|
||||||
|
luaL_pushresult(&buffer);
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
// On timeout return nil instead of an empty string
|
||||||
|
lua_pop(L, 1);
|
||||||
|
lua_pushnil(L);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
maybe_echo(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lua: string = tio.readline(timeout)
|
||||||
|
static int api_readline(lua_State *L) {
|
||||||
|
int timeout = lua_tointeger(L, 1); //ms
|
||||||
|
luaL_Buffer b;
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
if (timeout == 0)
|
||||||
|
{
|
||||||
|
timeout = -1; // Wait forever
|
||||||
|
}
|
||||||
|
|
||||||
|
luaL_buffinit(L, &b);
|
||||||
|
luaL_prepbuffer(&b);
|
||||||
|
while (true) {
|
||||||
|
int ret = read_poll(device_fd, &ch, 1, timeout);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return luaL_error(L, "%s", strerror(errno));
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
luaL_pushresult(&b);
|
||||||
|
maybe_echo(L);
|
||||||
|
lua_pushnil(L);
|
||||||
|
lua_insert(L, -2);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == '\n')
|
||||||
|
{
|
||||||
|
luaL_pushresult(&b);
|
||||||
|
maybe_echo(L);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
luaL_addchar(&b, ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lua: table = tio.ttysearch()
|
||||||
|
static int api_ttysearch(lua_State *L)
|
||||||
|
{
|
||||||
|
UNUSED(L);
|
||||||
|
GList *iter;
|
||||||
|
int i = 1;
|
||||||
|
|
||||||
|
GList *device_list = tty_search_for_serial_devices();
|
||||||
|
|
||||||
|
if (device_list == NULL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new table
|
||||||
|
lua_newtable(L);
|
||||||
|
|
||||||
|
// Iterate through found devices
|
||||||
|
for (iter = device_list; iter != NULL; iter = g_list_next(iter))
|
||||||
|
{
|
||||||
|
device_t *device = (device_t *) iter->data;
|
||||||
|
|
||||||
|
// Create a new sub-table for each serial device
|
||||||
|
lua_newtable(L);
|
||||||
|
|
||||||
|
// Add elements to the table
|
||||||
|
lua_pushstring(L, "path");
|
||||||
|
lua_pushstring(L, device->path);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
|
||||||
|
lua_pushstring(L, "tid");
|
||||||
|
lua_pushstring(L, device->tid);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
|
||||||
|
lua_pushstring(L, "uptime");
|
||||||
|
lua_pushnumber(L, device->uptime);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
|
||||||
|
lua_pushstring(L, "driver");
|
||||||
|
lua_pushstring(L, device->driver);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
|
||||||
|
lua_pushstring(L, "description");
|
||||||
|
lua_pushstring(L, device->description);
|
||||||
|
lua_settable(L, -3);
|
||||||
|
|
||||||
|
// Set the sub-table as a row in the main table
|
||||||
|
lua_rawseti(L, -2, i++);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return table
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void script_buffer_run(lua_State *L, const char *script_buffer)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = luaL_loadbuffer(L, script_buffer, strlen(script_buffer), "tio") ||
|
||||||
|
lua_pcall(L, 0, 0, 0);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
tio_warning_printf("lua: %s\n", lua_tostring(L, -1));
|
||||||
|
lua_pop(L, 1); /* Pop error message from the stack */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void script_file_run(lua_State *L, const char *filename)
|
||||||
|
{
|
||||||
|
if (strlen(filename) == 0)
|
||||||
|
{
|
||||||
|
tio_warning_printf("Missing script filename\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (luaL_dofile(L, filename))
|
||||||
|
{
|
||||||
|
tio_warning_printf("lua: %s", lua_tostring(L, -1));
|
||||||
|
lua_pop(L, 1); /* pop error message from the stack */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct luaL_Reg tio_lib[] =
|
||||||
|
{
|
||||||
|
{ "echo", api_echo},
|
||||||
|
{ "sleep", api_sleep},
|
||||||
|
{ "msleep", api_msleep},
|
||||||
|
{ "line_set", line_set},
|
||||||
|
{ "send", api_send},
|
||||||
|
{ "write", api_write},
|
||||||
|
{ "read", api_read},
|
||||||
|
{ "readline", api_readline},
|
||||||
|
{ "ttysearch", api_ttysearch},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void script_load(lua_State *L)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = luaL_loadbuffer(L, script_init, strlen(script_init), "tio") || lua_pcall(L, 0, 0, 0);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
tio_error_print("%s\n", lua_tostring(L, -1));
|
||||||
|
lua_pop(L, 1); // Pop error message from the stack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void script_set_global(lua_State *L, const char *name, long value)
|
||||||
|
{
|
||||||
|
lua_pushnumber(L, value);
|
||||||
|
lua_setglobal(L, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void script_set_globals(lua_State *L)
|
||||||
|
{
|
||||||
|
script_set_global(L, "toggle", 2);
|
||||||
|
script_set_global(L, "high", 1);
|
||||||
|
script_set_global(L, "low", 0);
|
||||||
|
script_set_global(L, "XMODEM_CRC", XMODEM_CRC);
|
||||||
|
script_set_global(L, "XMODEM_1K", XMODEM_1K);
|
||||||
|
script_set_global(L, "YMODEM", YMODEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM >= 502
|
||||||
|
static int luaopen_tio(lua_State *L)
|
||||||
|
{
|
||||||
|
luaL_newlib(L, tio_lib);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void script_run(int fd, const char *script_filename)
|
||||||
|
{
|
||||||
|
lua_State *L;
|
||||||
|
|
||||||
|
device_fd = fd;
|
||||||
|
|
||||||
|
L = luaL_newstate();
|
||||||
|
luaL_openlibs(L);
|
||||||
|
|
||||||
|
#if LUA_VERSION_NUM >= 502
|
||||||
|
luaL_requiref(L, "tio", luaopen_tio, 1);
|
||||||
|
#else
|
||||||
|
luaL_register(L, "tio", tio_lib);
|
||||||
|
#endif
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
// Load lua init script
|
||||||
|
script_load(L);
|
||||||
|
|
||||||
|
// Initialize globals
|
||||||
|
script_set_globals(L);
|
||||||
|
|
||||||
|
if (script_filename != NULL)
|
||||||
|
{
|
||||||
|
tio_printf("Running script %s", script_filename);
|
||||||
|
script_file_run(L, script_filename);
|
||||||
|
}
|
||||||
|
else if (option.script_filename != NULL)
|
||||||
|
{
|
||||||
|
tio_printf("Running script %s", option.script_filename);
|
||||||
|
script_file_run(L, option.script_filename);
|
||||||
|
}
|
||||||
|
else if (option.script != NULL)
|
||||||
|
{
|
||||||
|
tio_printf("Running script");
|
||||||
|
script_buffer_run(L, option.script);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_close(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *script_run_state_to_string(script_run_t state)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case SCRIPT_RUN_ONCE:
|
||||||
|
return "once";
|
||||||
|
case SCRIPT_RUN_ALWAYS:
|
||||||
|
return "always";
|
||||||
|
case SCRIPT_RUN_NEVER:
|
||||||
|
return "never";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/script.h
Normal file
33
src/script.h
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2024 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SCRIPT_RUN_ONCE,
|
||||||
|
SCRIPT_RUN_ALWAYS,
|
||||||
|
SCRIPT_RUN_NEVER,
|
||||||
|
SCRIPT_RUN_END,
|
||||||
|
} script_run_t;
|
||||||
|
|
||||||
|
void script_run(int fd, const char *script_filename);
|
||||||
|
const char *script_run_state_to_string(script_run_t state);
|
||||||
72
src/setspeed.c
Normal file
72
src/setspeed.c
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_TERMIOS2
|
||||||
|
#define termios asmtermios
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#undef termios
|
||||||
|
#include <asm-generic/ioctls.h>
|
||||||
|
#include <asm-generic/termbits.h>
|
||||||
|
|
||||||
|
#elif HAVE_IOSSIOSPEED
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <IOKit/serial/ioss.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_TERMIOS2
|
||||||
|
int setspeed(int fd, int baudrate)
|
||||||
|
{
|
||||||
|
struct termios2 tio;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status = ioctl(fd, TCGETS2, &tio);
|
||||||
|
|
||||||
|
// Set baudrate speed using termios2 interface
|
||||||
|
tio.c_cflag &= ~CBAUD;
|
||||||
|
tio.c_cflag |= BOTHER;
|
||||||
|
tio.c_ispeed = baudrate;
|
||||||
|
tio.c_ospeed = baudrate;
|
||||||
|
|
||||||
|
status = ioctl(fd, TCSETS2, &tio);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif HAVE_IOSSIOSPEED
|
||||||
|
int setspeed(int fd, int baudrate)
|
||||||
|
{
|
||||||
|
return ioctl(fd, IOSSIOSPEED, (char *)&baudrate);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
int setspeed(int fd, int baudrate)
|
||||||
|
{
|
||||||
|
UNUSED(fd);
|
||||||
|
UNUSED(baudrate);
|
||||||
|
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* tio - a simple TTY terminal I/O application
|
* tio - a serial device I/O tool
|
||||||
*
|
*
|
||||||
* Copyright (c) 2014-2017 Martin Lund
|
* Copyright (c) 2022 Martin Lund
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -19,10 +19,6 @@
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TIME_H
|
#pragma once
|
||||||
#define TIME_H
|
|
||||||
|
|
||||||
char * current_time(void);
|
int setspeed(int fd, int baudrate);
|
||||||
void delay(long ms);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* tio - a simple TTY terminal I/O application
|
* tio - a serial device I/O tool
|
||||||
*
|
*
|
||||||
* Copyright (c) 2014-2017 Martin Lund
|
* Copyright (c) 2022 Martin Lund
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -22,35 +22,30 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <signal.h>
|
||||||
#include "tio/error.h"
|
#include <errno.h>
|
||||||
#include "tio/print.h"
|
#include "error.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "tty.h"
|
||||||
|
|
||||||
char * current_time(void)
|
static void signal_handler(int signum)
|
||||||
{
|
{
|
||||||
static char time_string[20];
|
switch (signum)
|
||||||
time_t t;
|
|
||||||
struct tm *tmp;
|
|
||||||
|
|
||||||
t = time(NULL);
|
|
||||||
tmp = localtime(&t);
|
|
||||||
if (tmp == NULL)
|
|
||||||
{
|
{
|
||||||
error_printf("Retrieving local time failed");
|
case SIGHUP:
|
||||||
exit(EXIT_FAILURE);
|
tio_printf("Received SIGHUP signal!");
|
||||||
|
break;
|
||||||
|
case SIGINT:
|
||||||
|
tio_printf("Received SIGINT signal!");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
strftime(time_string, sizeof(time_string), "%H:%M:%S", tmp);
|
|
||||||
|
|
||||||
return time_string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void delay(long ms)
|
void signal_handlers_install(void)
|
||||||
{
|
{
|
||||||
struct timespec ts;
|
signal(SIGHUP, signal_handler);
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
ts.tv_sec = ms / 1000;
|
signal(SIGPIPE, SIG_IGN);
|
||||||
ts.tv_nsec = (ms % 1000) * 1000000;
|
|
||||||
|
|
||||||
nanosleep(&ts, NULL);
|
|
||||||
}
|
}
|
||||||
24
src/signals.h
Normal file
24
src/signals.h
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
void signal_handlers_install();
|
||||||
384
src/socket.c
Normal file
384
src/socket.c
Normal file
|
|
@ -0,0 +1,384 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
|
* Copyright (c) 2022 Google LLC
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "socket.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "tty.h"
|
||||||
|
|
||||||
|
#define MAX_SOCKET_CLIENTS 16
|
||||||
|
#define SOCKET_PORT_DEFAULT 3333
|
||||||
|
|
||||||
|
static int sockfd;
|
||||||
|
static int clientfds[MAX_SOCKET_CLIENTS];
|
||||||
|
static int socket_family = AF_UNSPEC;
|
||||||
|
static int port_number = SOCKET_PORT_DEFAULT;
|
||||||
|
|
||||||
|
static const char *socket_filename(void)
|
||||||
|
{
|
||||||
|
/* skip 'unix:' */
|
||||||
|
return option.socket + 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int socket_inet_port(void)
|
||||||
|
{
|
||||||
|
/* skip 'inet:' */
|
||||||
|
int port = atoi(option.socket + 5);
|
||||||
|
if (port == 0)
|
||||||
|
{
|
||||||
|
port = SOCKET_PORT_DEFAULT;
|
||||||
|
}
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int socket_inet6_port(void)
|
||||||
|
{
|
||||||
|
/* skip 'inet6:' */
|
||||||
|
int port = atoi(option.socket + 6);
|
||||||
|
if (port == 0)
|
||||||
|
{
|
||||||
|
port = SOCKET_PORT_DEFAULT;
|
||||||
|
}
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void socket_exit(void)
|
||||||
|
{
|
||||||
|
if (socket_family == AF_UNIX)
|
||||||
|
{
|
||||||
|
unlink(socket_filename());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool socket_stale(const char *path)
|
||||||
|
{
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
bool stale = false;
|
||||||
|
int sfd;
|
||||||
|
|
||||||
|
/* Test if socket file exists */
|
||||||
|
if (access(path, F_OK) == 0)
|
||||||
|
{
|
||||||
|
/* Create test socket */
|
||||||
|
sfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (sfd < 0)
|
||||||
|
{
|
||||||
|
tio_warning_printf("Failure opening socket (%s)", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare address */
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
|
||||||
|
|
||||||
|
/* Perform connect to test if socket is active */
|
||||||
|
if (connect(sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1)
|
||||||
|
{
|
||||||
|
if (errno == ECONNREFUSED)
|
||||||
|
{
|
||||||
|
// No one is listening on socket file
|
||||||
|
stale = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
close(sockfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket_configure(void)
|
||||||
|
{
|
||||||
|
struct sockaddr_un sockaddr_unix = {};
|
||||||
|
struct sockaddr_in sockaddr_inet = {};
|
||||||
|
struct sockaddr_in6 sockaddr_inet6 = {};
|
||||||
|
struct sockaddr *sockaddr_p;
|
||||||
|
socklen_t socklen;
|
||||||
|
int optval;
|
||||||
|
|
||||||
|
/* Parse socket string */
|
||||||
|
|
||||||
|
if (strncmp(option.socket, "unix:", 5) == 0)
|
||||||
|
{
|
||||||
|
socket_family = AF_UNIX;
|
||||||
|
|
||||||
|
if (strlen(socket_filename()) == 0)
|
||||||
|
{
|
||||||
|
tio_error_printf("Missing socket filename");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(socket_filename()) > sizeof(sockaddr_unix.sun_path) - 1)
|
||||||
|
{
|
||||||
|
tio_error_printf("Socket file path %s too long", option.socket);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(option.socket, "inet:", 5) == 0)
|
||||||
|
{
|
||||||
|
socket_family = AF_INET;
|
||||||
|
|
||||||
|
port_number = socket_inet_port();
|
||||||
|
|
||||||
|
if (port_number < 0)
|
||||||
|
{
|
||||||
|
tio_error_printf("Invalid port number: %d", port_number);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(option.socket, "inet6:", 6) == 0)
|
||||||
|
{
|
||||||
|
socket_family = AF_INET6;
|
||||||
|
|
||||||
|
port_number = socket_inet6_port();
|
||||||
|
|
||||||
|
if (port_number < 0)
|
||||||
|
{
|
||||||
|
tio_error_printf("Invalid port number: %d", port_number);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socket_family == AF_UNSPEC)
|
||||||
|
{
|
||||||
|
tio_error_printf("%s: Invalid socket scheme, must be prefixed with 'unix:', 'inet:', or 'inet6:'", option.socket);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure socket */
|
||||||
|
|
||||||
|
switch (socket_family)
|
||||||
|
{
|
||||||
|
case AF_UNIX:
|
||||||
|
sockaddr_unix.sun_family = AF_UNIX;
|
||||||
|
strncpy(sockaddr_unix.sun_path, socket_filename(), sizeof(sockaddr_unix.sun_path) - 1);
|
||||||
|
sockaddr_p = (struct sockaddr *) &sockaddr_unix;
|
||||||
|
socklen = sizeof(sockaddr_unix);
|
||||||
|
|
||||||
|
/* Test for stale unix socket file */
|
||||||
|
if (socket_stale(socket_filename()))
|
||||||
|
{
|
||||||
|
tio_printf("Cleaning up old socket file");
|
||||||
|
unlink(socket_filename());
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AF_INET:
|
||||||
|
sockaddr_inet.sin_family = AF_INET;
|
||||||
|
sockaddr_inet.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
sockaddr_inet.sin_port = htons(port_number);
|
||||||
|
sockaddr_p = (struct sockaddr *) &sockaddr_inet;
|
||||||
|
socklen = sizeof(sockaddr_inet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AF_INET6:
|
||||||
|
sockaddr_inet6.sin6_family = AF_INET6;
|
||||||
|
sockaddr_inet6.sin6_addr = in6addr_any;
|
||||||
|
sockaddr_inet6.sin6_port = htons(port_number);
|
||||||
|
sockaddr_p = (struct sockaddr *) &sockaddr_inet6;
|
||||||
|
socklen = sizeof(sockaddr_inet6);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
tio_error_printf("Invalid socket family (%d)", socket_family);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create socket */
|
||||||
|
sockfd = socket(socket_family, SOCK_STREAM, 0);
|
||||||
|
if (sockfd < 0)
|
||||||
|
{
|
||||||
|
tio_error_printf("Failed to create socket (%s)", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
|
||||||
|
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_NOSIGPIPE, &optval, sizeof(optval)))
|
||||||
|
#else
|
||||||
|
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)))
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
tio_error_printf("Failed to set socket options (%s)", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bind */
|
||||||
|
if (bind(sockfd, sockaddr_p, socklen) < 0)
|
||||||
|
{
|
||||||
|
tio_error_printf("Failed to bind to socket (%s)", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Listen */
|
||||||
|
if (listen(sockfd, MAX_SOCKET_CLIENTS) < 0)
|
||||||
|
{
|
||||||
|
tio_error_printf("Failed to listen on socket (%s)", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(clientfds, -1, sizeof(clientfds));
|
||||||
|
atexit(socket_exit);
|
||||||
|
|
||||||
|
if (socket_family == AF_UNIX)
|
||||||
|
{
|
||||||
|
tio_printf("Listening on socket %s", socket_filename());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tio_printf("Listening on socket port %d", port_number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket_write(char input_char)
|
||||||
|
{
|
||||||
|
if (!option.socket)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i != MAX_SOCKET_CLIENTS; ++i)
|
||||||
|
{
|
||||||
|
if (clientfds[i] != -1)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
|
||||||
|
if (send(clientfds[i], &input_char, 1, 0) <= 0)
|
||||||
|
#else
|
||||||
|
if (send(clientfds[i], &input_char, 1, MSG_NOSIGNAL) <= 0)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
tio_error_printf_silent("Failed to write to socket (%s)", strerror(errno));
|
||||||
|
close(clientfds[i]);
|
||||||
|
clientfds[i] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int socket_add_fds(fd_set *rdfs, bool connected)
|
||||||
|
{
|
||||||
|
if (!option.socket)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int numclients = 0, maxfd = 0;
|
||||||
|
for (int i = 0; i != MAX_SOCKET_CLIENTS; ++i)
|
||||||
|
{
|
||||||
|
if (clientfds[i] != -1)
|
||||||
|
{
|
||||||
|
/* let clients block if they try to send while we're disconnected */
|
||||||
|
if (connected)
|
||||||
|
{
|
||||||
|
FD_SET(clientfds[i], rdfs);
|
||||||
|
maxfd = MAX(maxfd, clientfds[i]);
|
||||||
|
}
|
||||||
|
numclients++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* don't bother to accept clients if we're already full */
|
||||||
|
if (numclients != MAX_SOCKET_CLIENTS)
|
||||||
|
{
|
||||||
|
FD_SET(sockfd, rdfs);
|
||||||
|
maxfd = MAX(maxfd, sockfd);
|
||||||
|
}
|
||||||
|
return maxfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool socket_handle_input(fd_set *rdfs, char *output_char)
|
||||||
|
{
|
||||||
|
if (!option.socket)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FD_ISSET(sockfd, rdfs))
|
||||||
|
{
|
||||||
|
int clientfd = accept(sockfd, NULL, NULL);
|
||||||
|
/* this loop should always succeed because we don't select on sockfd when full */
|
||||||
|
for (int i = 0; i != MAX_SOCKET_CLIENTS; ++i)
|
||||||
|
{
|
||||||
|
if (clientfds[i] == -1)
|
||||||
|
{
|
||||||
|
clientfds[i] = clientfd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i != MAX_SOCKET_CLIENTS; ++i)
|
||||||
|
{
|
||||||
|
if (clientfds[i] != -1 && FD_ISSET(clientfds[i], rdfs))
|
||||||
|
{
|
||||||
|
int status = read(clientfds[i], output_char, 1);
|
||||||
|
if (status == 0)
|
||||||
|
{
|
||||||
|
close(clientfds[i]);
|
||||||
|
clientfds[i] = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
tio_error_printf_silent("Failed to read from socket (%s)", strerror(errno));
|
||||||
|
close(clientfds[i]);
|
||||||
|
clientfds[i] = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If INLCR is set, a received NL character shall be translated into a CR character */
|
||||||
|
if (*output_char == '\n' && option.map_i_nl_cr)
|
||||||
|
{
|
||||||
|
*output_char = '\r';
|
||||||
|
}
|
||||||
|
else if (*output_char == '\r')
|
||||||
|
{
|
||||||
|
/* If IGNCR is set, a received CR character shall be ignored (not read). */
|
||||||
|
if (option.map_ign_cr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If IGNCR is not set and ICRNL is set, a received CR character shall be translated into an NL character. */
|
||||||
|
if (option.map_i_cr_nl)
|
||||||
|
{
|
||||||
|
*output_char = '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
31
src/socket.h
Normal file
31
src/socket.h
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
|
* Copyright (c) 2022 Google LLC
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
|
||||||
|
void socket_configure(void);
|
||||||
|
void socket_write(char input_char);
|
||||||
|
int socket_add_fds(fd_set *fds, bool connected);
|
||||||
|
bool socket_handle_input(fd_set *fds, char *output_char);
|
||||||
104
src/timestamp.c
Normal file
104
src/timestamp.c
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "error.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "timestamp.h"
|
||||||
|
|
||||||
|
char *timestamp_current_time(void)
|
||||||
|
{
|
||||||
|
static char time_string[TIME_STRING_SIZE_MAX];
|
||||||
|
static struct timeval tv, tv_now, tv_start, tv_previous;
|
||||||
|
static bool first = true;
|
||||||
|
struct tm *tm;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
// Get current time value
|
||||||
|
gettimeofday(&tv_now, NULL);
|
||||||
|
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
tv_start = tv_now;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add formatted timestamp
|
||||||
|
switch (option.timestamp)
|
||||||
|
{
|
||||||
|
case TIMESTAMP_NONE:
|
||||||
|
case TIMESTAMP_24HOUR:
|
||||||
|
// "hh:mm:ss.sss" (24 hour format)
|
||||||
|
tv = tv_now;
|
||||||
|
tm = localtime(&tv.tv_sec);
|
||||||
|
len = strftime(time_string, sizeof(time_string), "%H:%M:%S", tm);
|
||||||
|
break;
|
||||||
|
case TIMESTAMP_24HOUR_START:
|
||||||
|
// "hh:mm:ss.sss" (24 hour format relative to start time)
|
||||||
|
timersub(&tv_now, &tv_start, &tv);
|
||||||
|
tm = gmtime(&tv.tv_sec);
|
||||||
|
len = strftime(time_string, sizeof(time_string), "%H:%M:%S", tm);
|
||||||
|
break;
|
||||||
|
case TIMESTAMP_24HOUR_DELTA:
|
||||||
|
// "hh:mm:ss.sss" (24 hour format relative to previous time stamp)
|
||||||
|
timersub(&tv_now, &tv_previous, &tv);
|
||||||
|
tm = gmtime(&tv.tv_sec);
|
||||||
|
len = strftime(time_string, sizeof(time_string), "%H:%M:%S", tm);
|
||||||
|
break;
|
||||||
|
case TIMESTAMP_ISO8601:
|
||||||
|
// "YYYY-MM-DDThh:mm:ss.sss" (ISO-8601)
|
||||||
|
tv = tv_now;
|
||||||
|
tm = localtime(&tv.tv_sec);
|
||||||
|
len = strftime(time_string, sizeof(time_string), "%Y-%m-%dT%H:%M:%S", tm);
|
||||||
|
break;
|
||||||
|
case TIMESTAMP_EPOCH:
|
||||||
|
case TIMESTAMP_EPOCH_USEC:
|
||||||
|
// "N.sss" (seconds since Unix epoch, 1970-01-01 00:00:00Z)
|
||||||
|
tv = tv_now;
|
||||||
|
tm = localtime(&tv.tv_sec);
|
||||||
|
len = strftime(time_string, sizeof(time_string), "%s", tm);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append millis-/microseconds to all timestamps
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
if ( option.timestamp == TIMESTAMP_EPOCH_USEC )
|
||||||
|
{
|
||||||
|
len = snprintf(time_string + len, TIME_STRING_SIZE_MAX - len, ".%06ld", (long)tv.tv_usec);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
len = snprintf(time_string + len, TIME_STRING_SIZE_MAX - len, ".%03ld", (long)tv.tv_usec / 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Save previous time value for next run
|
||||||
|
tv_previous = tv_now;
|
||||||
|
|
||||||
|
return (len < TIME_STRING_SIZE_MAX) ? time_string : NULL;
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* tio - a simple TTY terminal I/O application
|
* tio - a serial device I/O tool
|
||||||
*
|
*
|
||||||
* Copyright (c) 2014-2017 Martin Lund
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -19,26 +19,21 @@
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TTY_H
|
#pragma once
|
||||||
#define TTY_H
|
|
||||||
|
|
||||||
#define KEY_QUESTION 0x3f
|
typedef enum
|
||||||
#define KEY_B 0x62
|
{
|
||||||
#define KEY_C 0x63
|
TIMESTAMP_NONE,
|
||||||
#define KEY_H 0x68
|
TIMESTAMP_24HOUR,
|
||||||
#define KEY_L 0x6C
|
TIMESTAMP_24HOUR_START,
|
||||||
#define KEY_Q 0x71
|
TIMESTAMP_24HOUR_DELTA,
|
||||||
#define KEY_S 0x73
|
TIMESTAMP_ISO8601,
|
||||||
#define KEY_T 0x74
|
TIMESTAMP_EPOCH,
|
||||||
#define KEY_CTRL_T 0x14
|
TIMESTAMP_EPOCH_USEC,
|
||||||
|
TIMESTAMP_END,
|
||||||
|
} timestamp_t;
|
||||||
|
|
||||||
#define NORMAL 0
|
#define TIME_STRING_SIZE_MAX 24
|
||||||
#define HEX 1
|
|
||||||
|
|
||||||
void stdout_configure(void);
|
char *timestamp_current_time(void);
|
||||||
void stdout_restore(void);
|
|
||||||
void tty_configure(void);
|
|
||||||
int tty_connect(void);
|
|
||||||
void tty_wait_for_device(void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
86
src/tty.h
Normal file
86
src/tty.h
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2022 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#define LINE_HIGH true
|
||||||
|
#define LINE_LOW false
|
||||||
|
|
||||||
|
#define TOPOLOGY_ID_SIZE 4
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
FLOW_NONE,
|
||||||
|
FLOW_HARD,
|
||||||
|
FLOW_SOFT,
|
||||||
|
} flow_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
PARITY_NONE,
|
||||||
|
PARITY_ODD,
|
||||||
|
PARITY_EVEN,
|
||||||
|
PARITY_MARK,
|
||||||
|
PARITY_SPACE,
|
||||||
|
} parity_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
AUTO_CONNECT_DIRECT,
|
||||||
|
AUTO_CONNECT_NEW,
|
||||||
|
AUTO_CONNECT_LATEST,
|
||||||
|
AUTO_CONNECT_END,
|
||||||
|
} auto_connect_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *tid;
|
||||||
|
double uptime;
|
||||||
|
char *path;
|
||||||
|
char *driver;
|
||||||
|
char *description;
|
||||||
|
} device_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int mask;
|
||||||
|
int value;
|
||||||
|
bool reserved;
|
||||||
|
} tty_line_config_t;
|
||||||
|
|
||||||
|
extern const char *device_name;
|
||||||
|
extern bool interactive_mode;
|
||||||
|
|
||||||
|
void stdout_configure(void);
|
||||||
|
void stdin_configure(void);
|
||||||
|
void tty_configure(void);
|
||||||
|
void tty_reconfigure(void);
|
||||||
|
int tty_connect(void);
|
||||||
|
void tty_wait_for_device(void);
|
||||||
|
void list_serial_devices(void);
|
||||||
|
void tty_input_thread_create(void);
|
||||||
|
void tty_input_thread_wait_ready(void);
|
||||||
|
void tty_line_set(int fd, tty_line_config_t line_config[]);
|
||||||
|
void tty_search(void);
|
||||||
|
GList *tty_search_for_serial_devices(void);
|
||||||
3
src/version.h.in
Normal file
3
src/version.h.in
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define VERSION "@VERSION@"
|
||||||
739
src/xymodem.c
Normal file
739
src/xymodem.c
Normal file
|
|
@ -0,0 +1,739 @@
|
||||||
|
/*
|
||||||
|
* Minimalistic implementation of the xmodem-1k and ymodem sender protocol.
|
||||||
|
* https://en.wikipedia.org/wiki/XMODEM
|
||||||
|
* https://en.wikipedia.org/wiki/YMODEM
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later OR MIT-0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include "xymodem.h"
|
||||||
|
#include "print.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
#define SOH 0x01
|
||||||
|
#define STX 0x02
|
||||||
|
#define ACK 0x06
|
||||||
|
#define NAK 0x15
|
||||||
|
#define CAN 0x18
|
||||||
|
#define EOT 0x04
|
||||||
|
|
||||||
|
#define SOH_STR "\001"
|
||||||
|
#define ACK_STR "\006"
|
||||||
|
#define NAK_STR "\025"
|
||||||
|
#define CAN_STR "\030"
|
||||||
|
#define EOT_STR "\004"
|
||||||
|
|
||||||
|
#define OK 0
|
||||||
|
#define ERR (-1)
|
||||||
|
#define ERR_FATAL (-2)
|
||||||
|
#define USER_CAN (-5)
|
||||||
|
|
||||||
|
#define RX_IGNORE 5
|
||||||
|
|
||||||
|
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
|
struct xpacket_1k {
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t seq;
|
||||||
|
uint8_t nseq;
|
||||||
|
uint8_t data[1024];
|
||||||
|
uint8_t crc_hi;
|
||||||
|
uint8_t crc_lo;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct xpacket {
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t seq;
|
||||||
|
uint8_t nseq;
|
||||||
|
uint8_t data[128];
|
||||||
|
uint8_t crc_hi;
|
||||||
|
uint8_t crc_lo;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* See https://en.wikipedia.org/wiki/Computation_of_cyclic_redundancy_checks */
|
||||||
|
static uint16_t crc16(const uint8_t *data, uint16_t size)
|
||||||
|
{
|
||||||
|
uint16_t crc, s;
|
||||||
|
|
||||||
|
for (crc = 0; size > 0; size--) {
|
||||||
|
s = *data++ ^ (crc >> 8);
|
||||||
|
s ^= (s >> 4);
|
||||||
|
crc = (crc << 8) ^ s ^ (s << 5) ^ (s << 12);
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xmodem_1k(int sio, const void *data, size_t len, int seq)
|
||||||
|
{
|
||||||
|
struct xpacket_1k packet;
|
||||||
|
const uint8_t *buf = data;
|
||||||
|
char resp = 0;
|
||||||
|
int rc, crc;
|
||||||
|
|
||||||
|
/* Drain pending characters from serial line. Insist on the
|
||||||
|
* last drained character being 'C'.
|
||||||
|
*/
|
||||||
|
while(1) {
|
||||||
|
if (key_hit)
|
||||||
|
return -1;
|
||||||
|
rc = read_poll(sio, &resp, 1, 50);
|
||||||
|
if (rc == 0) {
|
||||||
|
if (resp == 'C') break;
|
||||||
|
if (resp == CAN) return ERR;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (rc < 0) {
|
||||||
|
tio_error_print("Read sync from serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Always work with 1K packets */
|
||||||
|
packet.seq = seq;
|
||||||
|
packet.type = STX;
|
||||||
|
|
||||||
|
while (len) {
|
||||||
|
size_t sz, z = 0;
|
||||||
|
char *from, status;
|
||||||
|
|
||||||
|
/* Build next packet, pad with 0 to full seq */
|
||||||
|
z = min(len, sizeof(packet.data));
|
||||||
|
memcpy(packet.data, buf, z);
|
||||||
|
memset(packet.data + z, 0, sizeof(packet.data) - z);
|
||||||
|
crc = crc16(packet.data, sizeof(packet.data));
|
||||||
|
packet.crc_hi = crc >> 8;
|
||||||
|
packet.crc_lo = crc;
|
||||||
|
packet.nseq = 0xff - packet.seq;
|
||||||
|
|
||||||
|
/* Send packet */
|
||||||
|
from = (char *) &packet;
|
||||||
|
sz = sizeof(packet);
|
||||||
|
while (sz) {
|
||||||
|
if (key_hit)
|
||||||
|
return ERR;
|
||||||
|
if ((rc = write(sio, from, sz)) < 0 ) {
|
||||||
|
if (errno == EWOULDBLOCK) {
|
||||||
|
usleep(1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tio_error_print("Write packet to serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
from += rc;
|
||||||
|
sz -= rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear response */
|
||||||
|
resp = 0;
|
||||||
|
|
||||||
|
/* 'lrzsz' does not ACK ymodem's fin packet */
|
||||||
|
if (seq == 0 && packet.data[0] == 0) resp = ACK;
|
||||||
|
|
||||||
|
/* Read receiver response, timeout 1 s */
|
||||||
|
for(int n=0; n < 20; n++) {
|
||||||
|
if (key_hit)
|
||||||
|
return ERR;
|
||||||
|
rc = read_poll(sio, &resp, 1, 50);
|
||||||
|
if (rc < 0) {
|
||||||
|
tio_error_print("Read ack/nak from serial failed");
|
||||||
|
return ERR;
|
||||||
|
} else if(rc > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update "progress bar" */
|
||||||
|
switch (resp) {
|
||||||
|
case NAK: status = 'N'; break;
|
||||||
|
case ACK: status = '.'; break;
|
||||||
|
case 'C': status = 'C'; break;
|
||||||
|
case CAN: status = '!'; return ERR;
|
||||||
|
default: status = '?';
|
||||||
|
}
|
||||||
|
write(STDOUT_FILENO, &status, 1);
|
||||||
|
|
||||||
|
/* Move to next block after ACK */
|
||||||
|
if (resp == ACK) {
|
||||||
|
packet.seq++;
|
||||||
|
len -= z;
|
||||||
|
buf += z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send EOT at 1 Hz until ACK or CAN received */
|
||||||
|
while (seq) {
|
||||||
|
if (key_hit)
|
||||||
|
return ERR;
|
||||||
|
if (write(sio, EOT_STR, 1) < 0) {
|
||||||
|
tio_error_print("Write EOT to serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
write(STDOUT_FILENO, "|", 1);
|
||||||
|
/* 1s timeout */
|
||||||
|
rc = read_poll(sio, &resp, 1, 1000);
|
||||||
|
if (rc < 0) {
|
||||||
|
tio_error_print("Read from serial failed");
|
||||||
|
return ERR;
|
||||||
|
} else if(rc == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (resp == ACK || resp == CAN) {
|
||||||
|
write(STDOUT_FILENO, "\r\n", 2);
|
||||||
|
return (resp == ACK) ? OK : ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xmodem(int sio, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
struct xpacket packet;
|
||||||
|
const uint8_t *buf = data;
|
||||||
|
char resp = 0;
|
||||||
|
int rc, crc;
|
||||||
|
|
||||||
|
/* Drain pending characters from serial line. Insist on the
|
||||||
|
* last drained character being 'C'.
|
||||||
|
*/
|
||||||
|
while(1) {
|
||||||
|
if (key_hit)
|
||||||
|
return -1;
|
||||||
|
rc = read_poll(sio, &resp, 1, 50);
|
||||||
|
if (rc == 0) {
|
||||||
|
if (resp == 'C') break;
|
||||||
|
if (resp == CAN) return ERR;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (rc < 0) {
|
||||||
|
tio_error_print("Read sync from serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Always work with 128b packets */
|
||||||
|
packet.seq = 1;
|
||||||
|
packet.type = SOH;
|
||||||
|
|
||||||
|
while (len) {
|
||||||
|
size_t sz, z = 0;
|
||||||
|
char *from, status;
|
||||||
|
|
||||||
|
/* Build next packet, pad with 0 to full seq */
|
||||||
|
z = min(len, sizeof(packet.data));
|
||||||
|
memcpy(packet.data, buf, z);
|
||||||
|
memset(packet.data + z, 0, sizeof(packet.data) - z);
|
||||||
|
crc = crc16(packet.data, sizeof(packet.data));
|
||||||
|
packet.crc_hi = crc >> 8;
|
||||||
|
packet.crc_lo = crc;
|
||||||
|
packet.nseq = 0xff - packet.seq;
|
||||||
|
|
||||||
|
/* Send packet */
|
||||||
|
from = (char *) &packet;
|
||||||
|
sz = sizeof(packet);
|
||||||
|
while (sz) {
|
||||||
|
if (key_hit)
|
||||||
|
return ERR;
|
||||||
|
if ((rc = write(sio, from, sz)) < 0 ) {
|
||||||
|
if (errno == EWOULDBLOCK) {
|
||||||
|
usleep(1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tio_error_print("Write packet to serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
from += rc;
|
||||||
|
sz -= rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear response */
|
||||||
|
resp = 0;
|
||||||
|
|
||||||
|
/* Read receiver response, timeout 1 s */
|
||||||
|
for(int n=0; n < 20; n++) {
|
||||||
|
if (key_hit)
|
||||||
|
return ERR;
|
||||||
|
rc = read_poll(sio, &resp, 1, 50);
|
||||||
|
if (rc < 0) {
|
||||||
|
tio_error_print("Read ack/nak from serial failed");
|
||||||
|
return ERR;
|
||||||
|
} else if(rc > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update "progress bar" */
|
||||||
|
switch (resp) {
|
||||||
|
case NAK: status = 'N'; break;
|
||||||
|
case ACK: status = '.'; break;
|
||||||
|
case 'C': status = 'C'; break;
|
||||||
|
case CAN: status = '!'; return ERR;
|
||||||
|
default: status = '?';
|
||||||
|
}
|
||||||
|
write(STDOUT_FILENO, &status, 1);
|
||||||
|
|
||||||
|
/* Move to next block after ACK */
|
||||||
|
if (resp == ACK) {
|
||||||
|
packet.seq++;
|
||||||
|
len -= z;
|
||||||
|
buf += z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send EOT at 1 Hz until ACK or CAN received */
|
||||||
|
while (1) {
|
||||||
|
if (key_hit)
|
||||||
|
return ERR;
|
||||||
|
if (write(sio, EOT_STR, 1) < 0) {
|
||||||
|
tio_error_print("Write EOT to serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
write(STDOUT_FILENO, "|", 1);
|
||||||
|
/* 1s timeout */
|
||||||
|
rc = read_poll(sio, &resp, 1, 1000);
|
||||||
|
if (rc < 0) {
|
||||||
|
tio_error_print("Read from serial failed");
|
||||||
|
return ERR;
|
||||||
|
} else if(rc == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (resp == ACK || resp == CAN) {
|
||||||
|
write(STDOUT_FILENO, "\r\n", 2);
|
||||||
|
return (resp == ACK) ? OK : ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
|
int start_receive(int sio)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct pollfd fds;
|
||||||
|
fds.events = POLLIN;
|
||||||
|
fds.fd = sio;
|
||||||
|
for (int n = 0; n < 20; n++)
|
||||||
|
{
|
||||||
|
/* Send the 'C' byte until the sender of the file responds with
|
||||||
|
something. The start character will be sent once a second for a number of
|
||||||
|
seconds. If nothing is received in that time then return false to indicate
|
||||||
|
that the transfer did not start. */
|
||||||
|
rc = write(sio, "C", 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
if (errno == EWOULDBLOCK) {
|
||||||
|
usleep(1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tio_error_print("Write packet to serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
/* Wait until data is available */
|
||||||
|
rc = poll(&fds, 1, 3000);
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
tio_error_print("%s", strerror(errno));
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
else if (rc > 0)
|
||||||
|
{
|
||||||
|
if (fds.revents & POLLIN)
|
||||||
|
{
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (key_hit)
|
||||||
|
return USER_CAN;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t update_CRC(uint16_t crc, char data_char)
|
||||||
|
{
|
||||||
|
uint8_t data = data_char;
|
||||||
|
crc = crc ^ ((uint16_t)data << 8);
|
||||||
|
for (int ix = 0; (ix < 8); ix++)
|
||||||
|
{
|
||||||
|
if (crc & 0x8000)
|
||||||
|
{
|
||||||
|
crc = (crc << 1) ^ 0x1021;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
crc <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int receive_packet(int sio, struct xpacket packet, int fd)
|
||||||
|
{
|
||||||
|
char rxSeq1, rxSeq2 = 0;
|
||||||
|
char resp = 0;
|
||||||
|
uint16_t calcCrc = 0;
|
||||||
|
uint16_t rxCrc = 0;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
struct pollfd fds;
|
||||||
|
fds.events = POLLIN;
|
||||||
|
fds.fd = sio;
|
||||||
|
|
||||||
|
/* Read seq bytes*/
|
||||||
|
rc = read_poll(sio, &rxSeq1, 1, 3000);
|
||||||
|
if (rc == 0) {
|
||||||
|
tio_error_print("Timeout waiting for first seq byte");
|
||||||
|
return ERR;
|
||||||
|
} else if (rc < 0) {
|
||||||
|
tio_error_print("Error reading first seq byte")
|
||||||
|
return ERR_FATAL;
|
||||||
|
}
|
||||||
|
rc = read_poll(sio, &rxSeq2, 1, 3000);
|
||||||
|
if (rc == 0) {
|
||||||
|
tio_error_print("Timeout waiting for second seq byte");
|
||||||
|
return ERR;
|
||||||
|
} else if (rc < 0) {
|
||||||
|
tio_error_print("Error reading second seq byte")
|
||||||
|
return ERR_FATAL;
|
||||||
|
}
|
||||||
|
if (key_hit)
|
||||||
|
return USER_CAN;
|
||||||
|
|
||||||
|
/* Read packet Data */
|
||||||
|
for (unsigned ix = 0; (ix < sizeof(packet.data)); ix++)
|
||||||
|
{
|
||||||
|
rc = read_poll(sio, &resp, 1, 3000);
|
||||||
|
/* If the read times out or fails then fail this packet. */
|
||||||
|
if (rc == 0)
|
||||||
|
{
|
||||||
|
tio_error_print("Timeout waiting for next packet char");
|
||||||
|
rc = write(sio, CAN_STR, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
tio_error_print("Write cancel packet to serial failed");
|
||||||
|
return ERR_FATAL;
|
||||||
|
}
|
||||||
|
return ERR;
|
||||||
|
} else if (rc < 0) {
|
||||||
|
tio_error_print("Error reading next packet char")
|
||||||
|
rc = write(sio, CAN_STR, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
tio_error_print("Write cancel packet to serial failed");
|
||||||
|
}
|
||||||
|
return ERR_FATAL;
|
||||||
|
}
|
||||||
|
packet.data[ix] = (uint8_t) resp;
|
||||||
|
calcCrc = update_CRC(calcCrc, resp);
|
||||||
|
if (key_hit)
|
||||||
|
return USER_CAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read CRC */
|
||||||
|
rc = read_poll(sio, &resp, 1, 3000);
|
||||||
|
if (rc == 0) {
|
||||||
|
tio_error_print("Timeout waiting for first CRC byte");
|
||||||
|
return ERR;
|
||||||
|
} else if (rc < 0) {
|
||||||
|
tio_error_print("Error reading first CRC byte")
|
||||||
|
return ERR_FATAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t uresp = resp;
|
||||||
|
uint16_t uresp16 = uresp;
|
||||||
|
rxCrc = uresp16 << 8;
|
||||||
|
|
||||||
|
rc = read_poll(sio, &resp, 1, 3000);
|
||||||
|
if (rc == 0) {
|
||||||
|
tio_error_print("Timeout waiting for second CRC byte");
|
||||||
|
return ERR;
|
||||||
|
} else if (rc < 0) {
|
||||||
|
tio_error_print("Error reading second CRC byte")
|
||||||
|
return ERR_FATAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uresp = resp;
|
||||||
|
uresp16 = uresp;
|
||||||
|
rxCrc |= uresp16;
|
||||||
|
|
||||||
|
if (key_hit)
|
||||||
|
return USER_CAN;
|
||||||
|
|
||||||
|
/* At this point in the code, there should not be anything in the receive buffer
|
||||||
|
because the sender has just sent a complete packet and is waiting on a response. */
|
||||||
|
rc = poll(&fds, 1, 10);
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
tio_error_print("%s", strerror(errno));
|
||||||
|
tio_error_print("Poll check error after packet finish");
|
||||||
|
rc = write(sio, CAN_STR, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
tio_error_print("Write cancel packet to serial failed");
|
||||||
|
}
|
||||||
|
return ERR_FATAL;
|
||||||
|
}
|
||||||
|
else if (rc > 0)
|
||||||
|
{
|
||||||
|
if (fds.revents & POLLIN)
|
||||||
|
{
|
||||||
|
tio_error_print("RX sync error");
|
||||||
|
char dummy = 0;
|
||||||
|
/* Drain buffer */
|
||||||
|
while (read_poll(sio, &dummy, 1, 100) > 0) {}
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t tester = 0xff;
|
||||||
|
uint8_t seq1 = rxSeq1;
|
||||||
|
uint8_t seq2 = rxSeq2;
|
||||||
|
|
||||||
|
if ((calcCrc == rxCrc) && (seq1 == packet.seq - 1) && ((seq1 ^ seq2) == tester))
|
||||||
|
{
|
||||||
|
/* Resend of previously processed packet. */
|
||||||
|
rc = write(sio, ACK_STR, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
tio_error_print("Write acknowlegdement packet to serial failed");
|
||||||
|
return ERR_FATAL;
|
||||||
|
}
|
||||||
|
return RX_IGNORE;
|
||||||
|
}
|
||||||
|
else if ((calcCrc != rxCrc) || (seq1 != packet.seq) || ((seq1 ^ seq2) != tester))
|
||||||
|
{
|
||||||
|
/* Fail if the CRC or sequence number is not correct or if the two received
|
||||||
|
sequence numbers are not the complement of one another. */
|
||||||
|
tio_error_print("Bad CRC or sequence number");
|
||||||
|
tio_debug_printf("CRC read: %u", rxCrc);
|
||||||
|
tio_debug_printf("CRC calculated: %u", calcCrc);
|
||||||
|
tio_debug_printf("Seq read: %hhu", rxSeq1);
|
||||||
|
tio_debug_printf("Seq should be: %hhu", packet.seq);
|
||||||
|
tio_debug_printf("inv seq: %hhu", rxSeq2);
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* The data is good. Process the packet then ACK it to the sender. */
|
||||||
|
rc = write(fd, packet.data, sizeof(packet.data));
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
tio_error_print("Problem writing to file");
|
||||||
|
rc = write(sio, CAN_STR, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
tio_error_print("Write cancel packet to serial failed");
|
||||||
|
}
|
||||||
|
return ERR_FATAL;
|
||||||
|
}
|
||||||
|
rc = write(sio, ACK_STR, 1);
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
tio_error_print("Write acknowlegdement packet to serial failed");
|
||||||
|
return ERR_FATAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int xmodem_receive(int sio, int fd)
|
||||||
|
{
|
||||||
|
struct xpacket packet;
|
||||||
|
char resp = 0;
|
||||||
|
int rc;
|
||||||
|
bool complete = false;
|
||||||
|
char status;
|
||||||
|
|
||||||
|
/* Drain pending characters from serial line.*/
|
||||||
|
while(1) {
|
||||||
|
if (key_hit)
|
||||||
|
return -1;
|
||||||
|
rc = read_poll(sio, &resp, 1, 50);
|
||||||
|
if (rc == 0) {
|
||||||
|
if (resp == CAN) return ERR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (rc < 0) {
|
||||||
|
if (rc != USER_CAN) {
|
||||||
|
tio_error_print("Read sync from serial failed");
|
||||||
|
}
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Always work with 128b packets */
|
||||||
|
packet.seq = 1;
|
||||||
|
packet.type = SOH;
|
||||||
|
|
||||||
|
/* Start Receive*/
|
||||||
|
rc = start_receive(sio);
|
||||||
|
if (rc == 0)
|
||||||
|
{
|
||||||
|
tio_error_print("Timeout waiting for transfer to start");
|
||||||
|
return ERR;
|
||||||
|
} else if (rc < 0) {
|
||||||
|
tio_error_print("Error starting XMODEM receive");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!complete) {
|
||||||
|
/* Poll for 1 new byte for 3 seconds */
|
||||||
|
rc = read_poll(sio, &resp, 1, 3000);
|
||||||
|
if (rc == 0) {
|
||||||
|
tio_error_print("Timeout waiting for start of next packet");
|
||||||
|
return ERR;
|
||||||
|
} else if (rc < 0) {
|
||||||
|
tio_error_print("Error reading start of next packet")
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
if (key_hit)
|
||||||
|
return USER_CAN;
|
||||||
|
|
||||||
|
switch(resp)
|
||||||
|
{
|
||||||
|
case SOH:
|
||||||
|
/* Start of a packet */
|
||||||
|
rc = receive_packet(sio, packet, fd);
|
||||||
|
if (rc == OK) {
|
||||||
|
packet.seq++;
|
||||||
|
status = '.';
|
||||||
|
} else if (rc == ERR) {
|
||||||
|
rc = write(sio, NAK_STR, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
tio_error_print("Writing not acknowledge packet to serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
status = 'N';
|
||||||
|
} else if (rc == ERR_FATAL) {
|
||||||
|
tio_error_print("Receive cancelled due to fatal error");
|
||||||
|
return ERR;
|
||||||
|
} else if (rc == USER_CAN) {
|
||||||
|
rc = write(sio, CAN_STR, 1);
|
||||||
|
if (rc < 0) {
|
||||||
|
tio_error_print("Writing cancel to serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
return USER_CAN;
|
||||||
|
} else if (rc == RX_IGNORE) {
|
||||||
|
status = ':';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EOT:
|
||||||
|
/* End of Transfer */
|
||||||
|
rc = write(sio, ACK_STR, 1);
|
||||||
|
if (rc < 0)
|
||||||
|
{
|
||||||
|
tio_error_print("Write acknowlegdement packet to serial failed");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
complete = true;
|
||||||
|
status = '\0';
|
||||||
|
write(STDOUT_FILENO, "|\r\n", 3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CAN:
|
||||||
|
/* Cancel from sender */
|
||||||
|
tio_error_print("Transmission cancelled from sender");
|
||||||
|
return ERR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
tio_error_print("Unexpected character received waiting for next packet");
|
||||||
|
return ERR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Update "progress bar" */
|
||||||
|
write(STDOUT_FILENO, &status, 1);
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int xymodem_send(int sio, const char *filename, modem_mode_t mode)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
int rc, fd;
|
||||||
|
struct stat stat;
|
||||||
|
const uint8_t *buf;
|
||||||
|
|
||||||
|
/* Open file, map into memory */
|
||||||
|
fd = open(filename, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
tio_error_print("Could not open file");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
fstat(fd, &stat);
|
||||||
|
len = stat.st_size;
|
||||||
|
buf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||||
|
if (!buf) {
|
||||||
|
close(fd);
|
||||||
|
tio_error_print("Could not mmap file");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do transfer */
|
||||||
|
key_hit = 0;
|
||||||
|
if (mode == XMODEM_1K) {
|
||||||
|
rc = xmodem_1k(sio, buf, len, 1);
|
||||||
|
}
|
||||||
|
else if (mode == XMODEM_CRC) {
|
||||||
|
rc = xmodem(sio, buf, len);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Ymodem: hdr + file + fin */
|
||||||
|
while(1) {
|
||||||
|
char hdr[1024], *p;
|
||||||
|
|
||||||
|
rc = -1;
|
||||||
|
if (strlen(filename) > 977) break; /* hdr block overrun */
|
||||||
|
p = stpncpy(hdr, filename, 1024) + 1;
|
||||||
|
p += sprintf(p, "%ld %lo %o", len, stat.st_mtime, stat.st_mode);
|
||||||
|
|
||||||
|
if (xmodem_1k(sio, hdr, p - hdr, 0) < 0) break; /* hdr with metadata */
|
||||||
|
if (xmodem_1k(sio, buf, len, 1) < 0) break; /* xmodem file */
|
||||||
|
if (xmodem_1k(sio, "", 1, 0) < 0) break; /* empty hdr = fin */
|
||||||
|
rc = 0; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key_hit = 0xff;
|
||||||
|
|
||||||
|
/* Flush serial and release resources */
|
||||||
|
tcflush(sio, TCIOFLUSH);
|
||||||
|
munmap((void *)buf, len);
|
||||||
|
close(fd);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int xymodem_receive(int sio, const char *filename, modem_mode_t mode)
|
||||||
|
{
|
||||||
|
int rc, fd;
|
||||||
|
|
||||||
|
/* Create new file */
|
||||||
|
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);
|
||||||
|
if (fd < 0) {
|
||||||
|
tio_error_print("Could not open file");
|
||||||
|
return ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do transfer */
|
||||||
|
key_hit = 0;
|
||||||
|
if (mode == XMODEM_1K) {
|
||||||
|
tio_error_print("Not supported");
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
else if (mode == XMODEM_CRC) {
|
||||||
|
rc = xmodem_receive(sio, fd);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tio_error_print("Not supported");
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
key_hit = 0xff;
|
||||||
|
|
||||||
|
/* Flush serial and release resources */
|
||||||
|
tcflush(sio, TCIOFLUSH);
|
||||||
|
close(fd);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
34
src/xymodem.h
Normal file
34
src/xymodem.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* tio - a serial device I/O tool
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2024 Martin Lund
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
XMODEM_1K,
|
||||||
|
XMODEM_CRC,
|
||||||
|
YMODEM,
|
||||||
|
} modem_mode_t;
|
||||||
|
|
||||||
|
extern char key_hit;
|
||||||
|
|
||||||
|
int xymodem_send(int sio, const char *filename, modem_mode_t mode);
|
||||||
|
|
||||||
|
int xymodem_receive(int sio, const char *filename, modem_mode_t mode);
|
||||||
Loading…
Add table
Add a link
Reference in a new issue