{"action":"created","issue":{"url":"https://api.github.com/repos/openwrt/luci/issues/1674","repository_url":"https://api.github.com/repos/openwrt/luci","labels_url":"https://api.github.com/repos/openwrt/luci/issues/1674/labels{/name}","comments_url":"https://api.github.com/repos/openwrt/luci/issues/1674/comments","events_url":"https://api.github.com/repos/openwrt/luci/issues/1674/events","html_url":"https://github.com/openwrt/luci/issues/1674","id":303731438,"number":1674,"title":"\"Associated Stations\" - regex for parsing /etc/ethers fails for valid files","user":{"login":"jeffsf","id":1946364,"avatar_url":"https://avatars2.githubusercontent.com/u/1946364?v=4","gravatar_id":"","url":"https://api.github.com/users/jeffsf","html_url":"https://github.com/jeffsf","followers_url":"https://api.github.com/users/jeffsf/followers","following_url":"https://api.github.com/users/jeffsf/following{/other_user}","gists_url":"https://api.github.com/users/jeffsf/gists{/gist_id}","starred_url":"https://api.github.com/users/jeffsf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jeffsf/subscriptions","organizations_url":"https://api.github.com/users/jeffsf/orgs","repos_url":"https://api.github.com/users/jeffsf/repos","events_url":"https://api.github.com/users/jeffsf/events{/privacy}","received_events_url":"https://api.github.com/users/jeffsf/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":3,"created_at":"2018-03-09T05:46:23Z","updated_at":"2018-03-09T16:44:25Z","closed_at":null,"author_association":"NONE","body":"As written, LuCI fails to parse valid  `/etc/ethers` files. \r\n\r\nAs a result, the \"Associated Stations\" section does not reflect the contents of `/etc/ethers`\r\n\r\nAs per `ethers(5)`\r\n\r\n> The  two items are separated by any number of SPACE and/or TAB characters.  A # at the beginning of a line starts a comment which extends to the end of the line.  The Ethernet-address is written as `x:x:x:x:x:x`, where x is a hexadecimal number between 0 and  ff  which  represents one byte of the address, which is in network byte order (big-endian).  The IP-number may be a hostname which can be resolved by DNS or a dot separated number.\r\n\r\nA user or program that uses tabs will find that there is no host name / IP information as a result when there is not a DHCP lease entry (common in multi-AP settings, or where the `dnsmasq` on the device is not the DHCP server in use).\r\n\r\nIn `_nethints(what, callback)` in `lua/luci/sys.lua`\r\n\r\n```\r\n\tif fs.access(\"/etc/ethers\") then\r\n\t\tfor e in io.lines(\"/etc/ethers\") do\r\n\t\t\tmac, ip = e:match(\"^([a-f0-9]%S+) (%S+)\")\r\n\t\t\tif mac and ip then\r\n\t\t\t\t_add(what, mac:upper(), ip, nil, nil)\r\n\t\t\tend\r\n\t\tend\r\n\tend\r\n```\r\n\r\nWhile I'm not a LUA expert, that regex is looking for a single space between two non-space characters. I have confirmed this by testing with `/etc/ethers` and, for example, a tab character does not result in the UI picking up the second field, while a single space does. \r\n\r\nIt also appears to require lower-case characters, which it then immediately calls upper() on for display in the UI. I'm puzzled though, unless LUA does case-insensitive matching by default, as it seems to match on upper-case MAC addresses.\r\n\r\nI believe that regex could be better written as \r\n```\r\n^([a-fA-F0-9:]+)%s+(%S+)\r\n```\r\n(a LUA expert should check this, especially as the `A-F` may be redundant)\r\n\r\nYes, the `man` page doesn't specify that there can't be spaces at the start of the line, so the very strict or lax, depending on your perspective, might prefer \r\n```\r\n^%s*([a-fA-F0-9:]+)%s+(%S+)\r\n```\r\n"},"comment":{"url":"https://api.github.com/repos/openwrt/luci/issues/comments/371868144","html_url":"https://github.com/openwrt/luci/issues/1674#issuecomment-371868144","issue_url":"https://api.github.com/repos/openwrt/luci/issues/1674","id":371868144,"user":{"login":"jeffsf","id":1946364,"avatar_url":"https://avatars2.githubusercontent.com/u/1946364?v=4","gravatar_id":"","url":"https://api.github.com/users/jeffsf","html_url":"https://github.com/jeffsf","followers_url":"https://api.github.com/users/jeffsf/followers","following_url":"https://api.github.com/users/jeffsf/following{/other_user}","gists_url":"https://api.github.com/users/jeffsf/gists{/gist_id}","starred_url":"https://api.github.com/users/jeffsf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jeffsf/subscriptions","organizations_url":"https://api.github.com/users/jeffsf/orgs","repos_url":"https://api.github.com/users/jeffsf/repos","events_url":"https://api.github.com/users/jeffsf/events{/privacy}","received_events_url":"https://api.github.com/users/jeffsf/received_events","type":"User","site_admin":false},"created_at":"2018-03-09T16:44:25Z","updated_at":"2018-03-09T16:44:25Z","author_association":"NONE","body":"Note also that tab is the default for the common `cut` tool\r\n```\r\nCUT(1)                                                            User Commands                                                           CUT(1)\r\n\r\nNAME\r\n       cut - remove sections from each line of files\r\n\r\nSYNOPSIS\r\n       cut OPTION... [FILE]...\r\n\r\nDESCRIPTION\r\n       Print selected parts of lines from each FILE to standard output.\r\n\r\n       With no FILE, or when FILE is -, read standard input.\r\n\r\n       Mandatory arguments to long options are mandatory for short options too.\r\n\r\n       -b, --bytes=LIST\r\n              select only these bytes\r\n\r\n       -c, --characters=LIST\r\n              select only these characters\r\n\r\n       -d, --delimiter=DELIM\r\n              use DELIM instead of TAB for field delimiter\r\n```"}}
{"actor":{"display_login":"jeffsf"}}