29#include <sys/socket.h>
38#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
39#include <QtEnvironmentVariables>
79static std::string
lirc_trim(
const std::string& s);
81 size_t bs,
const std::string& name,
int line);
86 const std::string& token,
const std::string& token2,std::string& mode,
90 int (check)(std::string& s),
91 const std::string& name,
int line);
99 const std::string&
file,
100 const std::string& current_file);
102 const std::string&
file,
const std::string& current_file,
103 std::string& full_name);
108 const std::string&
file,
110 int (check)(std::string& s),
111 std::string& full_name,
112 std::string& sha_bang);
120static int sstrcasecmp(std::string s1, std::string s2);
122 std::string& button,
unsigned int rep);
124 struct lirc_config *config,
const std::string& code,
125 std::string&
string, std::string& prog);
129static int lirc_send_command(
const struct lirc_state *state,
int sockfd,
const std::string& command,
char *buf,
size_t *buf_len,
int *ret_status);
139 std::string lformat = state->
lirc_prog +
": " + format_str;
140 va_start(ap,format_str);
141 vfprintf(
stderr,lformat.data(),ap);
146 va_start(ap,format_str);
147 vfprintf(
stderr,format_str,ap);
164 struct sockaddr_un addr {};
168 if(lircrc_root_file==
nullptr || lircrc_user_file ==
nullptr || prog==
nullptr)
170 lirc_printf(
nullptr,
"%s: lirc_init invalid params\n",prog);
189 addr.sun_family=AF_UNIX;
190 strncpy(addr.sun_path,lircd,
sizeof(addr.sun_path)-1);
191 state->
lirc_lircd=socket(AF_UNIX,SOCK_STREAM,0);
199 if(connect(state->
lirc_lircd,(
struct sockaddr *)&addr,
sizeof(addr))==-1)
202 lirc_printf(state,
"could not connect to socket\n");
228 std::array<char,LIRC_READ+1> newline {};
232 char *ret=fgets(newline.data(),
LIRC_READ+1,f);
235 return(line.empty() ? -1 : 0);
237 line += newline.data();
238 if (line.back() ==
'\n')
248 const size_t start = s.find_first_not_of(
" \t");
249 if (start == std::string::npos)
251 const size_t end = s.find_last_not_of(
" \t");
252 if (end == std::string::npos)
253 return s.substr(start);
254 return s.substr(start, end-start+1);
260 size_t bs,
const std::string& name,
int line)
265 const char c=s[bs+1];
268 case 'a': i =
'\a';
break;
269 case 'b': i =
'\b';
break;
270 case 'e': i =
'\e';
break;
271 case 'f': i =
'\f';
break;
272 case 'n': i =
'\n';
break;
273 case 'r': i =
'\r';
break;
274 case 't': i =
'\t';
break;
275 case 'v': i =
'\v';
break;
290 i = std::stoi(s.substr(bs+1), &count, 8);
292 catch (std::out_of_range& e) {
295 if(i>(1<<CHAR_BIT)-1)
299 "out of range in %s:%d\n",name.c_str(),line);
304 i = std::stoi(s.substr(bs+2), &count, 16);
307 catch (std::invalid_argument& e) {
309 "following hex digits in %s:%d\n",
312 catch (std::out_of_range& e) {
320 if(i>(1<<CHAR_BIT)-1)
324 "of range in %s:%d\n",name.c_str(),line);
334 i =
static_cast<uint8_t
>(c);
338 s.erase(bs+1, count);
343 size_t bs = s.find_first_of(
'\\');
344 while(bs != std::string::npos)
347 bs = s.find_first_of(
'\\',bs);
352 [[maybe_unused]]
const std::string& name,
353 [[maybe_unused]]
int line)
359 if(s.front() !=
'"' && s.front() !=
'<')
363 if (((s.front() ==
'"') && (s.back() !=
'"')) ||
364 ((s.front() ==
'<') && (s.back() !=
'>')))
373 const std::string& token,
const std::string& token2,std::string& mode,
377 int (check)(std::string& s),
378 const std::string& name,
int line)
385 if(new_entry==
nullptr)
388 if(new_entry==
nullptr)
393 *new_config=new_entry;
398 "%s:%d\n",name.c_str(),line);
404 if(new_entry==
nullptr)
411 "%s:%d\n",name.c_str(),line);
416 else if(token ==
"end")
420 if(new_entry!=
nullptr)
423 if(new_entry->
prog.empty())
426 "config before line %d\n",
441 if(*last_config==
nullptr)
443 *first_config=new_entry;
444 *last_config=new_entry;
448 (*last_config)->
next=new_entry;
449 *last_config=new_entry;
464 if(check(list->
string)==-1)
481 "'begin'\n",name.c_str(),line);
489 if(new_entry!=
nullptr)
492 "'end' token\n",name.c_str(),line);
502 "match mode \"%s\"\n",
503 token2.c_str(),mode.c_str());
510 "'begin'\n",name.c_str(),line,token2.c_str());
517 lirc_printf(state,
"unknown token \"%s\" in %s:%d ignored\n",
518 token.c_str(),name.c_str(),line);
525 unsigned int flags=
none;
526 size_t start =
string.find_first_not_of(
" \t|");
527 while(start != std::string::npos)
529 size_t end =
string.find_first_of(
" \t|", start);
530 std::string s =
string.substr(start,end-start);
543 else if(s ==
"startup_mode")
547 else if(s ==
"toggle_reset")
553 lirc_printf(state,
"unknown flag \"%s\"\n",s.c_str());
555 start =
string.find_first_not_of(
" \t|", end);
561 const std::string&
file,
562 const std::string& current_file)
565 std::string home {
"/"};
569 if (qEnvironmentVariableIsSet(
"HOME"))
571 home = qgetenv(
"HOME").toStdString();
574 if(!home.empty() &&
filename.back()!=
'/')
580 else if(
file.starts_with(
"~/"))
582 if (qEnvironmentVariableIsSet(
"HOME"))
584 home = qgetenv(
"HOME").toStdString();
589 else if(
file[0]==
'/' || current_file.empty())
607 const std::string&
file,
const std::string& current_file,
608 std::string& full_name)
617 if(fin==
nullptr && (!
file.empty() || errno!=ENOENT))
619 lirc_printf(state,
"could not open config file %s\n",
623 else if(fin==
nullptr)
626 if(fin==
nullptr && errno!=ENOENT)
628 lirc_printf(state,
"could not open config file %s\n",
632 else if(fin==
nullptr)
634 lirc_printf(state,
"could not open config files %s and %s\n",
662 }
catch (
const std::bad_alloc& e) {
687 const std::string&
file,
689 int (check)(std::string& s))
691 struct sockaddr_un addr {};
693 unsigned int ret = 0;
696 std::string sha_bang;
707 addr.sun_family=AF_UNIX;
710 lirc_printf(state,
"WARNING: file name too long\n");
713 sockfd=socket(AF_UNIX,SOCK_STREAM,0);
716 lirc_printf(state,
"WARNING: could not open socket\n");
720 if(connect(sockfd, (
struct sockaddr *)&addr,
sizeof(addr))!=-1)
722 (*config)->sockfd=sockfd;
738 std::string command = sha_bang +
" " +
filename;
739 ret = system(command.data());
741 if(ret!=EXIT_SUCCESS)
744 sockfd=socket(AF_UNIX,SOCK_STREAM,0);
747 lirc_printf(state,
"WARNING: could not open socket\n");
751 if(connect(sockfd, (
struct sockaddr *)&addr,
sizeof(addr))!=-1)
755 (*config)->sockfd=sockfd;
766 const std::string&
file,
768 int (check)(std::string& s))
771 std::string sha_bang;
775static std::string
parse_token (
const std::string&
string,
size_t& next)
777 if (next == std::string::npos)
779 size_t start =
string.find_first_not_of(
" \t", next);
780 if (start == std::string::npos) {
781 next = std::string::npos;
784 next =
string.find_first_of(
" \t", start);
785 if (next == std::string::npos)
786 return string.substr(start);
787 return string.substr(start, next-start);
791 const std::string&
file,
793 int (check)(std::string& s),
794 std::string& full_name,
795 std::string& sha_bang)
801 if (filestack ==
nullptr)
807 if (filestack->
m_file ==
nullptr)
826 fclose(filestack->
m_file);
829 full_name = filestack->
m_name;
840 if(
string.starts_with(
"#!"))
842 sha_bang=
string.substr(2);
846 const size_t eq =
string.find_first_of(
'=');
847 if(eq == std::string::npos)
851 std::ranges::transform(token, token.begin(),
852 [](
char c){return std::tolower(c); });
857 else if(token ==
"include")
862 "included at %s:%d\n",
863 filestack->
m_name.c_str(),
869 std::string token2 {};
870 if (
next != std::string::npos)
871 token2 =
string.substr(
next);
874 (token2, filestack->
m_name,
878 if (stack_tmp ==
nullptr)
889 filestack = stack_tmp;
902 std::ranges::transform(token2, token2.begin(),
903 [](
char c){return std::tolower(c); });
904 if (
string.find_first_not_of(
" \t", next) != std::string::npos)
906 lirc_printf(state,
"unexpected token in line %s:%d\n",
912 &new_entry,&first,&last,
923 if(new_entry!=
nullptr)
935 std::string token=
lirc_trim(
string.substr(0,eq));
936 std::string token2=
lirc_trim(
string.substr(eq+1));
937 std::ranges::transform(token, token.begin(),
938 [](
char c){return std::tolower(c); });
943 else if(new_entry==
nullptr)
953 new_entry->
prog=token2;
955 else if(token ==
"remote")
966 else if(token ==
"button")
987 if(new_entry->
code==
nullptr)
989 new_entry->
code=code;
999 else if(token ==
"delay")
1002 bool fail {
false };
1004 new_entry->
rep_delay=std::stoi(token2,&end,0);
1006 catch (std::invalid_argument& e) { fail =
true; }
1007 catch (std::out_of_range& e) { fail =
true; }
1008 if(fail || (end == 0) || (token2[end] != 0))
1011 " a valid number for "
1012 "delay\n",token2.c_str());
1015 else if(token ==
"repeat")
1018 bool fail {
false };
1020 new_entry->
rep=std::stoi(token2,&end,0);
1022 catch (std::invalid_argument& e) { fail =
true; }
1023 catch (std::out_of_range& e) { fail =
true; }
1024 if(fail || (end == 0) || (token2[end] != 0))
1027 " a valid number for "
1028 "repeat\n",token2.c_str());
1031 else if(token ==
"config")
1036 new_list->string=token2;
1037 new_list->next=
nullptr;
1038 if(new_entry->
config==
nullptr)
1040 new_entry->
config=new_list;
1050 else if(token ==
"mode")
1054 else if(token ==
"flags")
1060 lirc_printf(state,
"unknown token \"%s\" in %s:%d ignored\n",
1061 token.c_str(),filestack->
m_name.c_str(),filestack->
m_line);
1067 if(new_entry!=
nullptr)
1073 ret=
lirc_mode(state,
"end",
"",mode,&new_entry,
1074 &first,&last,check,
"",0);
1075 lirc_printf(state,
"warning: end token missing at end "
1088 lirc_printf(state,
"warning: no end token found for mode "
1089 "\"%s\"\n",mode.c_str());
1096 if(*config==
nullptr)
1102 (*config)->first=first;
1103 (*config)->
next=first;
1105 (*config)->current_mode= startupmode;
1106 (*config)->sockfd=-1;
1123 std::string startupmode;
1127 while(
scan!=
nullptr)
1130 if(!
scan->change_mode.empty()) {
1131 startupmode=
scan->change_mode;
1133 scan->change_mode.clear();
1136 lirc_printf(state,
"startup_mode flags requires 'mode ='\n");
1142 if(startupmode.empty()) {
1144 while(
scan!=
nullptr)
1155 if(startupmode.empty())
return {};
1157 while(
scan!=
nullptr)
1159 if(!
scan->change_mode.empty() &&
1180 config->current_mode.clear();
1195 while(code!=
nullptr)
1205 while(list!=
nullptr)
1220 if(
config->current_mode.empty())
1225 while(
scan!=
nullptr)
1227 if(!
scan->change_mode.empty())
1236 config->current_mode.clear();
1249 if(!
scan->change_mode.empty())
1264 if(
scan->next_config!=
nullptr &&
1268 std::string s=
scan->next_config->string;
1269 scan->next_config=
scan->next_config->next;
1270 if(
scan->next_config==
nullptr)
1279 std::ranges::transform(s1, s1.begin(),
1280 [](
char c){ return std::tolower(c); });
1281 std::ranges::transform(s2, s2.begin(),
1282 [](
char c){ return std::tolower(c); });
1283 return(strcasecmp(s1.data(), s2.data()));
1287 std::string& button,
unsigned int rep)
1290 if(
scan->code==
nullptr)
1292 return static_cast<int>(
rep==0 ||
1306 if(
scan->code->next==
nullptr ||
rep==0)
1308 scan->next_code=
scan->next_code->next;
1309 if(
scan->code->next !=
nullptr)
1315 if(
scan->next_code==
nullptr)
1318 if(
scan->code->next!=
nullptr ||
rep==0 ||
1327 if(
rep!=0)
return 0;
1336 if(codes==
scan->next_code)
return 0;
1339 while(codes!=
scan->next_code->next)
1388 const std::string& code,std::string&
string)
1392 std::string command;
1393 static std::array<char,LIRC_PACKET_SIZE> s_buf;
1394 size_t buf_len = s_buf.size();
1402 s_buf.data(), &buf_len, &success);
1407 string = s_buf.data();
1422 struct lirc_config *config,
const std::string& code,
1423 std::string&
string, std::string& prog)
1425 unsigned int rep = 0;
1428 if(sscanf(code.c_str(),
"%*20x %20x %*5000s %*5000s\n",&rep)==1)
1431 size_t end = code.find_first_of(
" \t\n", 0);
1432 size_t start = code.find_first_not_of(
" \t\n",end);
1434 end = code.find_first_of(
" \t\n",start);
1435 start = code.find_first_not_of(
" \t\n",end);
1437 end = code.find_first_of(
" \t\n",start);
1438 std::string
button = code.substr(start,end-start);
1439 start = code.find_first_not_of(
" \t\n",end);
1441 end = code.find_first_of(
" \t\n",start);
1442 std::string
remote = code.substr(start,end-start);
1450 int quit_happened=0;
1452 while(
scan!=
nullptr)
1455 if(exec_level > 0 &&
1456 (
scan->mode.empty() ||
1457 (!
scan->mode.empty() &&
1513 static std::array<char,LIRC_PACKET_SIZE> s_buf;
1514 size_t buf_len = s_buf.size();
1518 s_buf.data(), &buf_len, &success);
1523 return s_buf.data();
1529 return config->current_mode;
1536 static std::array<char,LIRC_PACKET_SIZE> s_buf {};
1538 size_t buf_len = s_buf.size();
1546 s_buf.data(), &buf_len, &success);
1551 return s_buf.data();
1559 return config->current_mode;
1565 static std::array<char,LIRC_PACKET_SIZE+1> s_buffer;
1566 char *end =
nullptr;
1567 static size_t s_head=0;
1568 static size_t s_tail=0;
1572 struct timeval tv {};
1574 auto cleanup_fn = [&](
int *) {
1578 std::unique_ptr<int,
decltype(cleanup_fn)>
cleanup { &ret, cleanup_fn };
1582 memmove(s_buffer.data(),s_buffer.data()+s_head,s_tail-s_head+1);
1585 end=strchr(s_buffer.data(),
'\n');
1591 if(strlen(s_buffer.data())!=s_tail)
1609 ret=select(fd+1,&fds,
nullptr,
nullptr,&tv);
1610 while(ret==-1 && errno==EINTR)
1611 ret=select(fd+1,&fds,
nullptr,
nullptr,&tv);
1631 s_buffer[s_tail+n]=0;
1633 end=strchr(s_buffer.data(),
'\n');
1637 s_head=strlen(s_buffer.data())+1;
1639 return(s_buffer.data());
1645 unsigned long n = 0;
1646 unsigned long data_n=0;
1651 if(buf_len!=
nullptr)
1655 size_t todo=command.size();
1656 const char *data=command.data();
1657 lirc_printf(lstate,
"sending command: %s", command.data());
1660 ssize_t done=
write(sockfd,(
const void *) data,todo);
1674 bool good_packet =
false;
1675 bool bad_packet =
false;
1678 while(!good_packet && !bad_packet)
1682 if(
string==
nullptr)
return(-1);
1683 lirc_printf(lstate,
"read response: %s\n",
string);
1687 if(strcasecmp(
string,
"BEGIN")!=0)
1694 if(strncasecmp(
string,command.data(),strlen(
string))!=0 ||
1695 strlen(
string)+1!=command.size())
1703 if(strcasecmp(
string,
"SUCCESS")==0)
1707 else if(strcasecmp(
string,
"END")==0)
1713 else if(strcasecmp(
string,
"ERROR")==0)
1727 if(strcasecmp(
string,
"END")==0)
1732 else if(strcasecmp(
string,
"DATA")==0)
1742 data_n=std::stoul(
string,&end,0);
1744 catch (std::invalid_argument& ) { fail =
true; }
1745 catch (std::out_of_range& ) { fail =
true; }
1746 if(fail || (end == 0) || (
string[end] != 0))
1762 if(buf!=
nullptr && written+len+1<max)
1764 memcpy(buf+written,
string, len+1);
1768 if(n==data_n) state=
P_END;
1771 if(strcasecmp(
string,
"END")==0)
1787 if(ret_status!=
nullptr)
1791 if(buf_len!=
nullptr)
1795 return (
int) data_n;
1800 std::string command;
size_t lirc_getsocketname(const std::string &filename, char *buf, size_t size)
std::string lirc_getmode(const struct lirc_state *state, struct lirc_config *config)
static int lirc_iscode(struct lirc_config_entry *scan, std::string &remote, std::string &button, unsigned int rep)
static void lirc_perror(const struct lirc_state *)
static constexpr size_t LIRC_READ
static unsigned int lirc_flags(const struct lirc_state *state, const std::string &string)
int lirc_readconfig(const struct lirc_state *state, const std::string &file, struct lirc_config **config, int(check)(std::string &s))
static constexpr int8_t MAX_INCLUDES
static FILE * lirc_open(const struct lirc_state *state, const std::string &file, const std::string ¤t_file, std::string &full_name)
int lirc_deinit(struct lirc_state *state)
static void lirc_clearmode(struct lirc_config *config)
static void lirc_parse_include(std::string &s, const std::string &name, int line)
static std::string lirc_getfilename(const struct lirc_state *state, const std::string &file, const std::string ¤t_file)
static int lirc_identify(const struct lirc_state *state, int sockfd)
static struct filestack_t * stack_push(const struct lirc_state *state, struct filestack_t *parent)
void lirc_freeconfig(struct lirc_config *config)
static std::string lirc_trim(const std::string &s)
static const char * lirc_read_string(const struct lirc_state *state, int fd)
static std::string lirc_execute(const struct lirc_state *state, struct lirc_config *config, struct lirc_config_entry *scan)
static void lirc_parse_string(const struct lirc_state *state, std::string &s, const std::string &name, int line)
std::string lirc_setmode(const struct lirc_state *state, struct lirc_config *config, const std::string &mode)
static int sstrcasecmp(std::string s1, std::string s2)
static int lirc_mode(const struct lirc_state *state, const std::string &token, const std::string &token2, std::string &mode, struct lirc_config_entry **new_config, struct lirc_config_entry **first_config, struct lirc_config_entry **last_config, int(check)(std::string &s), const std::string &name, int line)
static std::string parse_token(const std::string &string, size_t &next)
static int lirc_send_command(const struct lirc_state *state, int sockfd, const std::string &command, char *buf, size_t *buf_len, int *ret_status)
static void lirc_printf(const struct lirc_state *, const char *format_str,...)
static int lirc_readconfig_only_internal(const struct lirc_state *state, const std::string &file, struct lirc_config **config, int(check)(std::string &s), std::string &full_name, std::string &sha_bang)
static void stack_free(struct filestack_t *entry)
static constexpr size_t LIRC_PACKET_SIZE
int lirc_readconfig_only(const struct lirc_state *state, const std::string &file, struct lirc_config **config, int(check)(std::string &s))
static int lirc_code2char_internal(const struct lirc_state *state, struct lirc_config *config, const std::string &code, std::string &string, std::string &prog)
int lirc_code2char(const struct lirc_state *state, struct lirc_config *config, const std::string &code, std::string &string)
static struct filestack_t * stack_pop(struct filestack_t *entry)
static int lirc_readline(const struct lirc_state *state, std::string &line, FILE *f)
static void lirc_freeconfigentries(struct lirc_config_entry *first)
struct lirc_state * lirc_init(const char *lircrc_root_file, const char *lircrc_user_file, const char *prog, const char *lircd, int verbose)
static std::string lirc_startupmode(const struct lirc_state *state, struct lirc_config_entry *first)
static void lirc_parse_escape(const struct lirc_state *state, std::string &s, size_t bs, const std::string &name, int line)
static constexpr int8_t LIRC_TIMEOUT
def read(device=None, features=[])
def scan(profile, smoonURL, gate)
def write(text, progress=True)
static QString cleanup(const QString &str)
struct filestack_t * m_parent
struct lirc_list * config
struct lirc_list * next_config
struct lirc_code * next_code
struct lirc_config_entry * next
std::string lircrc_root_file
std::string lircrc_user_file