Compare commits
22 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
199390d9fa | ||
|
|
9efd8cf9af | ||
|
|
9e984f6d38 | ||
|
|
df123ef150 | ||
|
|
f10a6900c6 | ||
|
|
22d3462f09 | ||
|
|
92cf2c603c | ||
|
|
6ff844b772 | ||
|
|
2b8da6aa30 | ||
|
|
99f98b165a | ||
|
|
0d7213d41a | ||
|
|
8c944a49af | ||
|
|
b742fa5ae5 | ||
|
|
1c8c3ac9d2 | ||
|
|
8486fa3984 | ||
|
|
8d3d8f8ceb | ||
|
|
8b48ebf39f | ||
|
|
1ca7ffc6e4 | ||
|
|
88b2260b90 | ||
|
|
a52826e00e | ||
|
|
52fcd6002c | ||
|
|
e7c1384079 |
27 changed files with 785 additions and 1617 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
zmidimap
|
||||
obj
|
||||
Zmakefile
|
||||
20
Makefile
20
Makefile
|
|
@ -11,15 +11,25 @@ CC=g++
|
|||
CXXFLAGS= -I$(IDIR) -Wall -pedantic -std=c++17
|
||||
ifeq ($(DEBUG),true)
|
||||
CXXFLAGS += -g
|
||||
else
|
||||
CXXFLAGS += -O2
|
||||
endif
|
||||
ifeq ($(STATIC),true)
|
||||
LDFLAGS += -l:libztd.a
|
||||
else
|
||||
LDFLAGS += -lztd
|
||||
endif
|
||||
|
||||
$(shell mkdir -p $(ODIR))
|
||||
$(shell mkdir -p $(BINDIR))
|
||||
|
||||
# automatically finds .hpp
|
||||
DEPS = $(shell if [ -n "$(ld $(IDIR))" ] ; then ls $(IDIR)/*.hpp ; fi)
|
||||
# automatically finds .cpp and makes the corresponding .o rule
|
||||
OBJ = $(shell ls $(SRCDIR)/*.cpp | sed 's/.cpp/.o/g;s|$(SRCDIR)/|$(ODIR)/|g')
|
||||
# automatically finds .h and .hpp
|
||||
DEPS = $(shell if [ -n "$(ld $(IDIR))" ] ; then ls $(IDIR)/*.hpp $(IDIR)/*.h 2>/dev/null ; fi)
|
||||
# automatically finds .c and .cpp and makes the corresponding .o rule
|
||||
OBJ = $(shell ls $(SRCDIR)/*.cpp $(SRCDIR)/*.c 2>/dev/null | sed 's|\.cpp|.o|g;s|\.c|.o|g;s|$(SRCDIR)/|$(ODIR)/|g')
|
||||
|
||||
$(ODIR)/%.o: $(SRCDIR)/%.c $(DEPS)
|
||||
$(CC) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
$(ODIR)/%.o: $(SRCDIR)/%.cpp $(DEPS)
|
||||
$(CC) $(CXXFLAGS) -c -o $@ $<
|
||||
|
|
@ -36,5 +46,5 @@ clean:
|
|||
clear:
|
||||
rm $(BINDIR)/$(NAME)
|
||||
|
||||
install: $(BINDIR)/$(NAME)
|
||||
install:
|
||||
mv $(BINDIR)/$(NAME) /usr/local/bin
|
||||
|
|
|
|||
15
README.md
15
README.md
|
|
@ -2,27 +2,26 @@
|
|||
|
||||
Map midi signals coming from ALSA midi devices to shell commands
|
||||
|
||||
Soft dependencies: alsa-utils
|
||||
Hard dependencies: aseqdump
|
||||
Dependencies: alsa-utils
|
||||
|
||||
## Installing
|
||||
|
||||
### Package
|
||||
|
||||
#### Debian/Ubuntu
|
||||
|
||||
Download the .deb package then run: `sudo dpkg -i zmidimap.deb ; sudo apt -f install`
|
||||
|
||||
### Standalone
|
||||
|
||||
Move executable `zmidimap` to your PATH directory
|
||||
Download ``zmidimap.tar.gz`` for a standalone executable
|
||||
|
||||
### From source
|
||||
|
||||
```sh
|
||||
git clone https://github.com/zawwz/zmidimap.git
|
||||
cd zmidimap
|
||||
make && sudo make install
|
||||
```
|
||||
Depends on: [ztd](https://github.com/zawwz/ztd)
|
||||
|
||||
``make -j6`` for a shared build
|
||||
``STATIC=true make -j6`` for a static build
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
|
|||
117
example.mim
117
example.mim
|
|
@ -1,78 +1,43 @@
|
|||
[
|
||||
Device "Launch Control"
|
||||
|
||||
// LAUNCH CONTROL
|
||||
{
|
||||
name=Launch Control
|
||||
commands=[
|
||||
// KNOBS HIGH
|
||||
{
|
||||
//KNOB HA
|
||||
//displays value 0:127 for knobs 21-28
|
||||
type=controller
|
||||
id=21:28
|
||||
shell=echo "Knob #$id ch$channel:$value"
|
||||
},
|
||||
// KNOBS LOW
|
||||
{
|
||||
//KNOB L1
|
||||
//displays value 0:127 for knob 41 from any channel
|
||||
type=controller
|
||||
id=41
|
||||
channel=*
|
||||
shell=echo "Knob #$id ch$channel:$value"
|
||||
},
|
||||
{
|
||||
//KNOB L2
|
||||
//displays value -100:100 for knob 42 on channel 0
|
||||
type=controller
|
||||
id=42
|
||||
channel=0
|
||||
remap=-100:100
|
||||
shell=echo "Knob #$id ch$channel:$value r:$rawvalue"
|
||||
},
|
||||
{
|
||||
//KNOB L3 H1
|
||||
//displays value 0:1:0 for knob 42 on channel 1 (first half)
|
||||
type=controller
|
||||
id=42
|
||||
channel=1
|
||||
range=0:63
|
||||
remap=0:1
|
||||
float=true
|
||||
shell=echo "Knob #$id ch$channel:$value"
|
||||
},
|
||||
{
|
||||
//KNOB L3 H2
|
||||
//displays value 0:1:0 for knob 42 on channel 1 (second half)
|
||||
type=controller
|
||||
id=42
|
||||
channel=1
|
||||
range=64:127
|
||||
remap=1:0
|
||||
float=true
|
||||
shell=echo "Knob #$id ch$channel:$value"
|
||||
}
|
||||
]
|
||||
}
|
||||
,
|
||||
// LAUNCHPAD
|
||||
{
|
||||
name=Launchpad S
|
||||
commands=[
|
||||
{
|
||||
// ANY NOTE ON
|
||||
type=note
|
||||
id=*
|
||||
shell=echo "Note $id on velocity:$velocity"
|
||||
},
|
||||
{
|
||||
// ANY NOTE OFF
|
||||
type=note
|
||||
id=*
|
||||
trigger=0
|
||||
shell=echo "Note $id off"
|
||||
}
|
||||
]
|
||||
}
|
||||
//display value 0:127 for knobs 21-28
|
||||
Command controller
|
||||
id=21:28
|
||||
shell='echo "Knob #$id ch$channel:$value"'
|
||||
|
||||
]
|
||||
//display value -100:100 for knob 41 on channel 0
|
||||
Command controller
|
||||
id=41
|
||||
channel=0
|
||||
remap=-100:100
|
||||
shell='echo "Knob #$id ch0:$value r:$rawvalue"'
|
||||
|
||||
//display value 0:1:0 for knob 41 on channel 1 (first half)
|
||||
Command controller
|
||||
id=42
|
||||
channel=1
|
||||
range=0:63
|
||||
remap=0:1
|
||||
float=true
|
||||
shell='echo "Knob #$id ch1:$value r:$rawvalue"'
|
||||
|
||||
//display value 0:1:0 for knob 41 on channel 1 (second half)
|
||||
Command controller
|
||||
id=42
|
||||
channel=1
|
||||
range=64:127
|
||||
remap=1:0
|
||||
float=true
|
||||
shell='echo "Knob #$id ch1:$value r:$rawvalue"'
|
||||
|
||||
|
||||
Device "Launchpad S"
|
||||
|
||||
//display any note pressed and its velocity
|
||||
Command note
|
||||
shell=echo Note $id on velocity:$velocity
|
||||
|
||||
//display any note off
|
||||
Command note
|
||||
trigger=0
|
||||
shell=echo Note $id off
|
||||
|
|
|
|||
67
example.zfd
Normal file
67
example.zfd
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
[
|
||||
|
||||
// LAUNCH CONTROL
|
||||
{
|
||||
name=Launch Control
|
||||
commands=[
|
||||
// KNOBS HIGH
|
||||
{
|
||||
//KNOB HA
|
||||
//displays value 0:127 for knobs 21-28
|
||||
type=controller
|
||||
id=21:28
|
||||
shell='echo "Knob #$id ch$channel:$value"'
|
||||
},
|
||||
{
|
||||
//KNOB L2
|
||||
//displays value -100:100 for knob 41 on channel 0
|
||||
type=controller
|
||||
id=42
|
||||
channel=0
|
||||
remap=-100:100
|
||||
shell='echo "Knob #$id ch0:$value r:$rawvalue"'
|
||||
},
|
||||
{
|
||||
//KNOB L3 H1
|
||||
//displays value 0:1:0 for knob 41 on channel 1 (first half)
|
||||
type=controller
|
||||
id=42
|
||||
channel=1
|
||||
range=0:63
|
||||
remap=0:1
|
||||
float=true
|
||||
shell='echo "Knob #$id ch1:$value r:$rawvalue"'
|
||||
},
|
||||
{
|
||||
//KNOB L3 H2
|
||||
//displays value 0:1:0 for knob 41 on channel 1 (second half)
|
||||
type=controller
|
||||
id=42
|
||||
channel=1
|
||||
range=64:127
|
||||
remap=1:0
|
||||
float=true
|
||||
shell='echo "Knob #$id ch1:$value r:$rawvalue"'
|
||||
}
|
||||
]
|
||||
}
|
||||
,
|
||||
// LAUNCHPAD
|
||||
{
|
||||
name=Launchpad S
|
||||
commands=[
|
||||
{
|
||||
// ANY NOTE ON
|
||||
type=note
|
||||
shell=echo Note $id on velocity:$velocity
|
||||
},
|
||||
{
|
||||
// ANY NOTE OFF
|
||||
type=note
|
||||
trigger=0
|
||||
shell=echo Note $id off
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
|
|
@ -1,10 +1,38 @@
|
|||
FORMAT_FOLDER=help_format
|
||||
IDIR=include
|
||||
|
||||
cp $FORMAT_FOLDER/help_template_head $IDIR/help.h
|
||||
SLASHSCRIPT='s|\\|\\\\|g;s|\"|\\\"|g'
|
||||
NEWLINESCRIPT=':a;N;$!ba;s/\n/\\n/g;'
|
||||
|
||||
echo "#define FILE_FORMAT \"zmidimap$(sed -n -e 'H;${x;s/\n/\\n/g;s/^,//;p;}' $FORMAT_FOLDER/file-format)\"" >> $IDIR/help.h
|
||||
echo "#define SHELL_FORMAT \"zmidimap$(sed -n -e 'H;${x;s/\n/\\n/g;s/^,//;p;}' $FORMAT_FOLDER/shell-format)\"" >> $IDIR/help.h
|
||||
echo "#define COMMAND_TAGS \"zmidimap$(sed -n -e 'H;${x;s/\n/\\n/g;s/^,//;p;}' $FORMAT_FOLDER/command-tags)\"" >> $IDIR/help.h
|
||||
filetocstr()
|
||||
{
|
||||
sed -e $SLASHSCRIPT "$1" | sed $NEWLINESCRIPT
|
||||
}
|
||||
|
||||
cat $FORMAT_FOLDER/help_template_tail >> $IDIR/help.h
|
||||
gen_line()
|
||||
{
|
||||
name="$(basename "$1")"
|
||||
echo "#define $(echo "$name" | tr '[:lower:]' '[:upper:]') \"$(filetocstr "$1")\""
|
||||
}
|
||||
|
||||
help_header()
|
||||
{
|
||||
echo '#ifndef HELP_H
|
||||
#define HELP_
|
||||
'
|
||||
}
|
||||
|
||||
help_footer()
|
||||
{
|
||||
echo '
|
||||
#endif //HELP_H'
|
||||
}
|
||||
|
||||
help_header > "$IDIR/help.h"
|
||||
|
||||
for I in help_format/*
|
||||
do
|
||||
gen_line "$I" >> "$IDIR/help.h"
|
||||
done
|
||||
|
||||
help_footer >> "$IDIR/help.h"
|
||||
|
|
|
|||
|
|
@ -1,14 +1,5 @@
|
|||
-- [COMMAND TAGS] --
|
||||
|
||||
[Global tags]
|
||||
type=<type>
|
||||
shell=<shell command>
|
||||
--
|
||||
*type: type of the signal: note/controller/pitch/system/connect/disconnect
|
||||
> mandatory
|
||||
*shell: shell command to be executed
|
||||
> mandatory
|
||||
|
||||
[Note tags]
|
||||
id=<interval>
|
||||
channel=<x/*>
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
-- [FILE FORMAT] --
|
||||
|
||||
[<device>,<device>]
|
||||
|
||||
<device> format
|
||||
{
|
||||
name=<name>
|
||||
commands=[<command>,<command>]
|
||||
}
|
||||
--
|
||||
*name: string referring to client name of the device
|
||||
|
||||
<command> format
|
||||
{
|
||||
<tag>=<value>
|
||||
<tag>=<value>
|
||||
...
|
||||
}
|
||||
--
|
||||
value can be concatenated with \"\" or ''
|
||||
see command-tags
|
||||
|
||||
Full info on file format: https://github.com/zawwz/zFiledat
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
#ifndef HELP_H
|
||||
#define HELP_H
|
||||
|
|
@ -1 +0,0 @@
|
|||
#endif //HELP_H
|
||||
26
help_format/mim_format
Normal file
26
help_format/mim_format
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
-- [MIM FILE FORMAT] --
|
||||
|
||||
Device <name>
|
||||
Command <type>
|
||||
shell=<shell command>
|
||||
<tag>=<value>
|
||||
<tag>=<value>
|
||||
...
|
||||
Command <type>
|
||||
...
|
||||
|
||||
Device <name>
|
||||
...
|
||||
|
||||
--
|
||||
*name: string referring to client name of the device
|
||||
> mandatory
|
||||
*type: type of the signal: note/controller/pitch/system/connect/disconnect
|
||||
> mandatory
|
||||
*shell: shell command to be executed
|
||||
> mandatory
|
||||
|
||||
Shell commands can be concatenated with "" or ''
|
||||
Comments are written with // or # and end at end of line
|
||||
Multiple tags can be put on the same line by separating them with ;
|
||||
See --command-tags for optional command tags
|
||||
30
help_format/zfd_format
Normal file
30
help_format/zfd_format
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
-- [ZFD FILE FORMAT] --
|
||||
|
||||
ZFD format: http://zawz.net/doc/ztd/zfd.html
|
||||
|
||||
[<device>,<device>]
|
||||
|
||||
-- <device> format --
|
||||
{
|
||||
name=<name>
|
||||
commands=[<command>,<command>]
|
||||
}
|
||||
--
|
||||
*name: string referring to client name of the device
|
||||
|
||||
<command> format
|
||||
{
|
||||
type=<type>
|
||||
shell=<shell command>
|
||||
<tag>=<value>
|
||||
<tag>=<value>
|
||||
...
|
||||
}
|
||||
--
|
||||
*type: type of the signal: note/controller/pitch/system/connect/disconnect
|
||||
> mandatory
|
||||
*shell: shell command to be executed
|
||||
> mandatory
|
||||
|
||||
Shell commands can be concatenated with "" or ''
|
||||
see --command-tags for optional command tags
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
#ifndef FILEDAT_H
|
||||
#define FILEDAT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
class format_error : public std::exception
|
||||
{
|
||||
public:
|
||||
format_error(const std::string& what, const std::string& origin, const std::string& data, int where) { desc=what; index=where; filename=origin; sdat=data; }
|
||||
|
||||
const char * what () const throw () {return desc.c_str();}
|
||||
const int where () const throw () {return index;}
|
||||
const char * data() const throw () {return sdat.c_str();}
|
||||
const char * origin() const throw () {return filename.c_str();}
|
||||
private:
|
||||
std::string desc;
|
||||
int index;
|
||||
std::string filename;
|
||||
std::string sdat;
|
||||
};
|
||||
|
||||
void printErrorIndex(const char* in, const int index, const std::string& message, const std::string& origin);
|
||||
inline void printFormatException(format_error& exc) { printErrorIndex(exc.data(), exc.where(), exc.what(), exc.origin()); }
|
||||
|
||||
class Filedat;
|
||||
|
||||
class AbstractChunk
|
||||
{
|
||||
public:
|
||||
enum typeEnum { none, val, chunk, list};
|
||||
|
||||
typeEnum type() { return m_type; }
|
||||
|
||||
AbstractChunk() { m_type=AbstractChunk::none; }
|
||||
virtual ~AbstractChunk() {}
|
||||
|
||||
protected:
|
||||
typeEnum m_type;
|
||||
};
|
||||
|
||||
class Chunk
|
||||
{
|
||||
public:
|
||||
Chunk() { m_achunk=nullptr; }
|
||||
|
||||
Chunk(const char* in)
|
||||
{ m_achunk=nullptr; set(in, strlen(in), 0, nullptr); }
|
||||
Chunk(std::string const& in)
|
||||
{ m_achunk=nullptr; set(in, 0, nullptr); }
|
||||
Chunk(const char* in, const int in_size, int offset=0, Filedat* data=nullptr)
|
||||
{ m_achunk=nullptr; set(in, in_size, offset, data); }
|
||||
Chunk(std::string const& in, int offset=0, Filedat* data=nullptr)
|
||||
{ m_achunk=nullptr; set(in, offset, data); }
|
||||
|
||||
Chunk(Chunk const& in) { m_achunk=nullptr; set(in); }
|
||||
|
||||
void clear() { if(m_achunk!=nullptr) delete m_achunk; m_achunk=nullptr; }
|
||||
~Chunk() { clear(); }
|
||||
|
||||
Filedat* parent() const { return m_parent; }
|
||||
int offset() const { return m_offset; }
|
||||
|
||||
void set(const char* in, const int in_size, int offset=0, Filedat* data=nullptr);
|
||||
void set(std::string const& in, int offset=0, Filedat* data=nullptr) { this->set(in.c_str(), in.size(), offset, data); }
|
||||
|
||||
void set(Chunk const& in) { this->set(in.strval(), in.offset(), in.parent()); } // TODO
|
||||
|
||||
std::string strval(unsigned int alignment=0, std::string const& aligner="\t") const;
|
||||
|
||||
|
||||
void addToChunk(std::string const& name, Chunk const& val); //adds if datachunk
|
||||
void addToChunk(std::vector<std::pair<std::string, Chunk>> const& vec); //adds if datachunk
|
||||
void addToList(Chunk const& val); //adds if list
|
||||
void addToList(std::vector<Chunk> const& vec); //adds if list
|
||||
inline void add(std::string const& name, Chunk const& val) { addToChunk(name, val); } //adds if datachunk
|
||||
inline void add(std::pair<std::string, Chunk> const& pair) { add(pair.first, pair.second); } //adds if datachunk
|
||||
inline void add(std::vector<std::pair<std::string, Chunk>> const& vec) { addToChunk(vec); } //adds if datachunk
|
||||
inline void add(Chunk const& val) { addToList(val); } //adds if list
|
||||
inline void add(std::vector<Chunk> const& vec) { addToList(vec); } //adds if list
|
||||
// void concatenate(Chunk const& chk); //concatenates chunks
|
||||
|
||||
Chunk copy() const { return Chunk(*this); }
|
||||
Chunk* pcopy() const { return new Chunk(*this); }
|
||||
|
||||
AbstractChunk* getp() const { return m_achunk; }
|
||||
AbstractChunk::typeEnum type() const { if(m_achunk!=nullptr) return m_achunk->type(); else return AbstractChunk::none; }
|
||||
int listSize() const;
|
||||
|
||||
Chunk* subChunkPtr(std::string const& a) const; //datachunk
|
||||
Chunk* subChunkPtr(unsigned int a) const; //chunklist
|
||||
Chunk& subChunkRef(std::string const& a) const; //datachunk
|
||||
Chunk& subChunkRef(unsigned int a) const; //chunklist
|
||||
|
||||
Chunk& operator[](std::string const& a) const { return subChunkRef(a); }
|
||||
Chunk& operator[](unsigned int a) const { return subChunkRef(a); }
|
||||
Chunk& operator=(Chunk const& a) { set(a); return *this; }
|
||||
inline Chunk& operator+=(std::pair<std::string, Chunk> const& a) { add(a); return *this; }
|
||||
inline Chunk& operator+=(std::vector<std::pair<std::string, Chunk>> const& a) { add(a); return *this; }
|
||||
inline Chunk& operator+=(Chunk const& a) { add(a); return *this; }
|
||||
inline Chunk& operator+=(std::vector<Chunk> const& a) { add(a); return *this; }
|
||||
// inline bool operator*=(Chunk const& a) { concatenate(a); }
|
||||
|
||||
//add operator+ and operator*
|
||||
|
||||
|
||||
protected:
|
||||
Filedat* m_parent;
|
||||
int m_offset;
|
||||
|
||||
AbstractChunk* m_achunk;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, Chunk const& a);
|
||||
|
||||
class DataVal : public AbstractChunk
|
||||
{
|
||||
public:
|
||||
DataVal() { m_type=AbstractChunk::val; }
|
||||
virtual ~DataVal() {}
|
||||
|
||||
std::string val;
|
||||
};
|
||||
|
||||
class DataChunk : public AbstractChunk
|
||||
{
|
||||
public:
|
||||
DataChunk() { m_type=AbstractChunk::chunk; }
|
||||
virtual ~DataChunk();
|
||||
|
||||
std::map<std::string, Chunk*> values;
|
||||
|
||||
};
|
||||
|
||||
class ChunkList : public AbstractChunk
|
||||
{
|
||||
public:
|
||||
ChunkList() { m_type=AbstractChunk::list; }
|
||||
virtual ~ChunkList();
|
||||
|
||||
std::vector<Chunk*> list;
|
||||
};
|
||||
|
||||
class Filedat
|
||||
{
|
||||
public:
|
||||
Filedat();
|
||||
Filedat(std::string const& in);
|
||||
virtual ~Filedat();
|
||||
|
||||
bool readTest() const;
|
||||
|
||||
void import_file(const std::string& path="");
|
||||
void import_stdin();
|
||||
void import_string(const std::string& data);
|
||||
bool export_file(std::string const& path="", std::string const& aligner="\t") const;
|
||||
|
||||
void clear();
|
||||
|
||||
inline std::string filePath() const { return m_filePath; }
|
||||
inline void setFilePath(std::string const& in) { m_filePath=in; }
|
||||
|
||||
std::string strval(std::string const& aligner="\t") const;
|
||||
|
||||
inline Chunk* pchunk() const { return m_dataChunk; }
|
||||
inline Chunk& chunk() const { return *m_dataChunk; }
|
||||
|
||||
inline Chunk* pdata() const { return m_dataChunk; }
|
||||
inline Chunk& data() const { return *m_dataChunk; }
|
||||
|
||||
inline const std::string& stringdata() const { return m_data; }
|
||||
inline const char* c_data() const { return m_data.c_str(); }
|
||||
|
||||
inline Chunk& operator[](const std::string& index) { return m_dataChunk->subChunkRef(index); }
|
||||
inline Chunk& operator[](const int index) { return m_dataChunk->subChunkRef(index); }
|
||||
|
||||
private:
|
||||
//functions
|
||||
void generateChunk();
|
||||
//attributes
|
||||
std::string m_filePath;
|
||||
std::string m_data;
|
||||
Chunk* m_dataChunk;
|
||||
};
|
||||
|
||||
#endif //FILEDAT_H
|
||||
|
|
@ -6,12 +6,7 @@
|
|||
#include <thread>
|
||||
|
||||
#include "command.hpp"
|
||||
#include "Filedat.hpp"
|
||||
|
||||
#define KILL_COMMAND_FH "kill -s INT $(pgrep -f \"aseqdump -p "
|
||||
#define KILL_COMMAND_SH "\")"
|
||||
|
||||
void sh(std::string const& string);
|
||||
#include <ztd/filedat.hpp>
|
||||
|
||||
class Device
|
||||
{
|
||||
|
|
@ -22,8 +17,8 @@ public:
|
|||
bool start_loop();
|
||||
void run_signal(char* buff);
|
||||
|
||||
bool import_chunk(Chunk const& ch);
|
||||
Chunk export_chunk();
|
||||
bool import_chunk(const ztd::chunkdat& ch);
|
||||
ztd::chunkdat export_chunk();
|
||||
|
||||
std::string name;
|
||||
int client_id;
|
||||
|
|
@ -37,11 +32,13 @@ public:
|
|||
std::vector<ConnectCommand> connectCommands;
|
||||
std::vector<DisconnectCommand> disconnectCommands;
|
||||
|
||||
std::thread thread;
|
||||
pid_t thread_pid;
|
||||
private:
|
||||
static void loop(Device* dev);
|
||||
};
|
||||
|
||||
extern std::vector<Device*> device_list;
|
||||
|
||||
void clean_devices();
|
||||
|
||||
#endif //DEVICE_HPP
|
||||
|
|
|
|||
14
include/format.hpp
Normal file
14
include/format.hpp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef FORMAT_HPP
|
||||
#define FORMAT_HPP
|
||||
|
||||
#include <ztd/filedat.hpp>
|
||||
|
||||
ztd::chunkdat mimtochk(std::string mim);
|
||||
|
||||
std::string chktomim(ztd::chunkdat const& chk, std::string const& aligner);
|
||||
|
||||
std::string file_strimport(const std::string& path);
|
||||
|
||||
bool is_mim(const std::string& str);
|
||||
|
||||
#endif //FORMAT_HPP
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
#ifndef HELP_H
|
||||
#define HELP_H
|
||||
#define FILE_FORMAT "zmidimap\n-- [FILE FORMAT] --\n\n[<device>,<device>]\n\n<device> format\n {\n name=<name>\n commands=[<command>,<command>]\n }\n--\n *name: string referring to client name of the device\n\n<command> format\n {\n <tag>=<value>\n <tag>=<value>\n ...\n }\n--\n value can be concatenated with \"\" or ''\n see command-tags\n\nFull info on file format: https://github.com/zawwz/zFiledat"
|
||||
#define SHELL_FORMAT "zmidimap\n-- [SHELL FORMAT] --\n\nShell command follows regular shell format\n\n-- [Environment] --\n\n[Note]\n $id: id of the note\n $channel: channel of the note\n $velocity: velocity of the note\n\n[Controller]\n $value: value of the controller (remapped)\n $id: id of the controller\n $channel: channel of the controller\n $rawvalue: original value of the controller\n\n[Pitch]\n $value: value of the bend (remapped)\n $rawvalue: original value of the bend\n\n[System]\n $code: hexadecimal code of the system signal"
|
||||
#define COMMAND_TAGS "zmidimap\n-- [COMMAND TAGS] --\n\n[Global tags]\n type=<type>\n shell=<shell command>\n--\n *type: type of the signal: note/controller/pitch/system/connect/disconnect\n > mandatory\n *shell: shell command to be executed\n > mandatory\n\n[Note tags]\n id=<interval>\n channel=<x/*>\n trigger=<interval>\n--\n *id: note id from 0 to 127\n > optional, default 0:127\n *channel: value from 0 to 16 for channel, * for any channel\n > optional, default *\n *trigger: note velocity from 0 to 127 that triggers the command\n > optional, default 1:127\n\n[Controller tags]\n id=<interval>\n channel=<x/*>\n range=<interval>\n remap=<interval>\n float=<true/false>\n--\n *id: controller id from 0 to 127\n > optional, default 0:127\n *channel: value from 0 to 16 for channel, * for any channel\n > optional, default *\n *range: controller value from 0 to 127 that triggers command\n > optional, default 0:127\n *remap: remaps the range to given interval\n > optional, default same as range\n *float: boolean value defining if output is a floating point value\n > optional, default false\n\n[Pitch bend tags]\n range=<interval>\n remap=<interval>\n float=<true/false>\n--\n *range: controller value from -8192 to 8191 that triggers command\n > optional, default -8192:8191\n *remap: remaps the range to given interval\n > optional, default same as range\n *float: boolean value defining if output is a floating point value\n > optional, default false\n\n[Interval Format]\n x:y range from x to y\n x single value x\n * all possible values"
|
||||
#define HELP_
|
||||
|
||||
#define COMMAND_TAGS "-- [COMMAND TAGS] --\n\n[Note tags]\n id=<interval>\n channel=<x/*>\n trigger=<interval>\n--\n *id: note id from 0 to 127\n > optional, default 0:127\n *channel: value from 0 to 16 for channel, * for any channel\n > optional, default *\n *trigger: note velocity from 0 to 127 that triggers the command\n > optional, default 1:127\n\n[Controller tags]\n id=<interval>\n channel=<x/*>\n range=<interval>\n remap=<interval>\n float=<true/false>\n--\n *id: controller id from 0 to 127\n > optional, default 0:127\n *channel: value from 0 to 16 for channel, * for any channel\n > optional, default *\n *range: controller value from 0 to 127 that triggers command\n > optional, default 0:127\n *remap: remaps the range to given interval\n > optional, default same as range\n *float: boolean value defining if output is a floating point value\n > optional, default false\n\n[Pitch bend tags]\n range=<interval>\n remap=<interval>\n float=<true/false>\n--\n *range: controller value from -8192 to 8191 that triggers command\n > optional, default -8192:8191\n *remap: remaps the range to given interval\n > optional, default same as range\n *float: boolean value defining if output is a floating point value\n > optional, default false\n\n[Interval Format]\n x:y range from x to y\n x single value x\n * all possible values"
|
||||
#define MIM_FORMAT "-- [MIM FILE FORMAT] --\n\nDevice <name>\n Command <type>\n shell=<shell command>\n <tag>=<value>\n <tag>=<value>\n ...\n Command <type>\n ...\n\nDevice <name>\n...\n\n--\n*name: string referring to client name of the device\n > mandatory\n*type: type of the signal: note/controller/pitch/system/connect/disconnect\n > mandatory\n*shell: shell command to be executed\n > mandatory\n\nShell commands can be concatenated with \"\" or ''\nComments are written with // or # and end at end of line\nMultiple tags can be put on the same line by separating them with ;\nSee --command-tags for optional command tags"
|
||||
#define SHELL_FORMAT "-- [SHELL FORMAT] --\n\nShell command follows regular shell format\n\n-- [Environment] --\n\n[Note]\n $id: id of the note\n $channel: channel of the note\n $velocity: velocity of the note\n\n[Controller]\n $value: value of the controller (remapped)\n $id: id of the controller\n $channel: channel of the controller\n $rawvalue: original value of the controller\n\n[Pitch]\n $value: value of the bend (remapped)\n $rawvalue: original value of the bend\n\n[System]\n $code: hexadecimal code of the system signal"
|
||||
#define ZFD_FORMAT "-- [ZFD FILE FORMAT] --\n\nZFD format: http://zawz.net/doc/ztd/zfd.html\n\n[<device>,<device>]\n\n-- <device> format --\n {\n name=<name>\n commands=[<command>,<command>]\n }\n--\n*name: string referring to client name of the device\n\n<command> format\n {\n type=<type>\n shell=<shell command>\n <tag>=<value>\n <tag>=<value>\n ...\n }\n--\n*type: type of the signal: note/controller/pitch/system/connect/disconnect\n > mandatory\n*shell: shell command to be executed\n > mandatory\n\nShell commands can be concatenated with \"\" or ''\nsee --command-tags for optional command tags"
|
||||
|
||||
#endif //HELP_H
|
||||
|
|
|
|||
12
include/log.hpp
Normal file
12
include/log.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef LOG_HPP
|
||||
#define LOG_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#define DEFAULT_LOG_STATE true
|
||||
|
||||
extern bool log_on;
|
||||
|
||||
void log(const std::string& str);
|
||||
|
||||
#endif //LOG_HPP
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
#ifndef OPTIONS_H
|
||||
#define OPTIONS_H
|
||||
|
||||
//DUPLICATES NOT HANDLED: takes last one
|
||||
//NO ORDER: no way to know options order
|
||||
//RETURNS TRANSITIONAL STATE IF ERROR
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
|
||||
// Turn argc/argv into a vector<string>
|
||||
std::vector<std::string> argVector(int argc, char** argv);
|
||||
|
||||
class Option
|
||||
{
|
||||
public:
|
||||
/* CTOR/DTOR */
|
||||
//ctors
|
||||
Option();
|
||||
Option(char c, bool arg, std::string helptext="", std::string argname="arg");
|
||||
Option(std::string const& str, bool arg, std::string helptext="", std::string argname="arg");
|
||||
Option(char c, std::string const& str, bool arg, std::string helptext="", std::string argname="arg");
|
||||
//dtors
|
||||
virtual ~Option();
|
||||
|
||||
/* FUNCTIONS */
|
||||
|
||||
// Print command help. Puts leftpad spaces before printing, and rightpad space until help
|
||||
void printHelp(int leftpad, int rightpad);
|
||||
|
||||
/* PROPERTIES */
|
||||
|
||||
bool shortDef; // has a char definition
|
||||
char charName;
|
||||
|
||||
bool longDef; // has a string definition
|
||||
std::string strName;
|
||||
|
||||
bool takesArgument; // option takes an argument
|
||||
|
||||
std::string help_text; // text to display in printHelp
|
||||
std::string arg_name; // name of the argument to display in printHelp
|
||||
|
||||
/* PROCESSING STATUS */
|
||||
|
||||
bool activated; // option was activated
|
||||
|
||||
std::string argument; // argument of the option
|
||||
|
||||
};
|
||||
|
||||
class OptionSet
|
||||
{
|
||||
public:
|
||||
/* CTOR/DTOR */
|
||||
OptionSet();
|
||||
virtual ~OptionSet();
|
||||
|
||||
/* PROPERTIES */
|
||||
|
||||
// Stream on which errors are sent. Default stderr
|
||||
std::ostream* errStream;
|
||||
|
||||
/* FUNCTIONS */
|
||||
|
||||
/*CREATION FUNCTIONS*/
|
||||
//Adding an option. Refer to Option ctors
|
||||
void addOption(Option opt) { m_options.push_back(opt); }
|
||||
|
||||
/*PRINT FUNCTIONS*/
|
||||
// Print command help. Puts leftpad spaces before each line, and rightpad space until help
|
||||
void printHelp(int leftpad=2, int rightpad=25);
|
||||
|
||||
/*QUERY FUNCTIONS*/
|
||||
// Find an option with its charname
|
||||
Option* findOption(char c);
|
||||
// Find an option with its stringname
|
||||
Option* findOption(std::string const& str);
|
||||
|
||||
/*PROCESSING FUNCTIONS*/
|
||||
// Process through options.
|
||||
// pair.first : vector with arguments that were not identified as options
|
||||
// pair.second : bool indicating status. True if no error encountered, false if errors
|
||||
std::pair<std::vector<std::string>,bool> getOptions(std::vector<std::string> input);
|
||||
std::pair<std::vector<std::string>,bool> getOptions(int argc, char** argv) { return getOptions(argVector(argc, argv)); }
|
||||
|
||||
private:
|
||||
std::vector<Option> m_options;
|
||||
|
||||
};
|
||||
|
||||
#endif //OPTIONS_H
|
||||
|
|
@ -5,8 +5,16 @@
|
|||
#define LIST_COMMAND "aseqdump -l | tail -n +2 | cut -c10-42 | tr -s ' '"
|
||||
#define LIST_EXTENDED_COMMAND "aseqdump -l | tail -n +2 | cut -c-42"
|
||||
|
||||
#include <string>
|
||||
|
||||
extern int announce_thread_pid;
|
||||
|
||||
void kill_all();
|
||||
|
||||
void device_check();
|
||||
|
||||
void announce_loop();
|
||||
|
||||
void filetime_loop(std::string const& filepath);
|
||||
|
||||
#endif //SYSTEM_HPP
|
||||
|
|
|
|||
815
src/Filedat.cpp
815
src/Filedat.cpp
|
|
@ -1,815 +0,0 @@
|
|||
#include "Filedat.hpp"
|
||||
|
||||
#include <exception>
|
||||
|
||||
bool _isRead(char in)
|
||||
{
|
||||
bool out=false;
|
||||
if(in>=33 && in<=126)
|
||||
out=true;
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string _repeatString(const std::string& str, const unsigned int n)
|
||||
{
|
||||
std::string ret;
|
||||
for(unsigned int i=0 ; i<n ; i++)
|
||||
ret += str;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void printErrorIndex(const char* in, const int index, const std::string& message, const std::string& origin)
|
||||
{
|
||||
int i=0, j=0; // j: last newline
|
||||
int line=1; //n: line #
|
||||
int in_size=strlen(in);
|
||||
if(index >= 0)
|
||||
{
|
||||
while(i < in_size && i < index)
|
||||
{
|
||||
if(in[i] == '\n')
|
||||
{
|
||||
line++;
|
||||
j=i+1;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
while(i < in_size && in[i]!='\n')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if(origin != "")
|
||||
{
|
||||
std::cerr << origin << ": Error\nLine " << line << " col " << index-j+1 << ": " << message << std::endl;
|
||||
std::cerr << std::string(in+j, i-j) << std::endl;
|
||||
std::cerr << _repeatString(" ", index-j) << '^' << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Format Error: " << message << std::endl;
|
||||
if(index >= 0)
|
||||
{
|
||||
std::cerr << std::string(in, i) << std::endl;
|
||||
std::cerr << _repeatString(" ", index-j) << '^' << std::endl;
|
||||
}
|
||||
else
|
||||
std::cerr << in << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Filedat::Filedat()
|
||||
{
|
||||
m_dataChunk = new Chunk();
|
||||
}
|
||||
|
||||
Filedat::Filedat(std::string const& in)
|
||||
{
|
||||
m_dataChunk = new Chunk();
|
||||
m_filePath=in;
|
||||
}
|
||||
|
||||
Filedat::~Filedat()
|
||||
{
|
||||
if(m_dataChunk!=nullptr)
|
||||
delete m_dataChunk;
|
||||
}
|
||||
|
||||
void Filedat::clear()
|
||||
{
|
||||
m_data="";
|
||||
if(m_dataChunk!=nullptr)
|
||||
{
|
||||
delete m_dataChunk;
|
||||
m_dataChunk = new Chunk();
|
||||
}
|
||||
}
|
||||
|
||||
bool Filedat::readTest() const
|
||||
{
|
||||
std::ifstream stream(m_filePath);
|
||||
if(!stream)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
void Filedat::import_file(const std::string& path)
|
||||
{
|
||||
if(path != "")
|
||||
m_filePath=path;
|
||||
std::ifstream st(m_filePath);
|
||||
if(!st)
|
||||
throw std::runtime_error("Cannot read file '" + m_filePath + '\'');
|
||||
|
||||
this->clear();
|
||||
std::string line;
|
||||
|
||||
while(st)
|
||||
{
|
||||
getline(st, line);
|
||||
m_data += (line + '\n');
|
||||
}
|
||||
this->generateChunk();
|
||||
}
|
||||
|
||||
void Filedat::import_stdin()
|
||||
{
|
||||
m_filePath="stdin";
|
||||
this->clear();
|
||||
std::string line;
|
||||
while(std::cin)
|
||||
{
|
||||
getline(std::cin, line);
|
||||
m_data += (line + '\n');
|
||||
}
|
||||
this->generateChunk();
|
||||
}
|
||||
|
||||
void Filedat::import_string(const std::string& data)
|
||||
{
|
||||
this->clear();
|
||||
m_data=data;
|
||||
m_filePath="";
|
||||
this->generateChunk();
|
||||
}
|
||||
|
||||
bool Filedat::export_file(std::string const& path, std::string const& aligner) const
|
||||
{
|
||||
std::ofstream stream;
|
||||
if(path=="")
|
||||
stream.open(m_filePath);
|
||||
else
|
||||
stream.open(path);
|
||||
if(!stream)
|
||||
return false;
|
||||
stream << this->strval(aligner);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Filedat::strval(std::string const& aligner) const
|
||||
{
|
||||
if(m_dataChunk == nullptr)
|
||||
return "";
|
||||
else
|
||||
return m_dataChunk->strval(0, aligner);
|
||||
}
|
||||
|
||||
void Filedat::generateChunk()
|
||||
{
|
||||
try
|
||||
{
|
||||
if(m_dataChunk != nullptr)
|
||||
delete m_dataChunk;
|
||||
m_dataChunk = new Chunk(m_data.c_str(), m_data.size(), 0, this);
|
||||
}
|
||||
catch(format_error& e)
|
||||
{
|
||||
throw format_error(e.what(), m_filePath, m_data, e.where());
|
||||
}
|
||||
}
|
||||
|
||||
std::string _getname(const char* in, const int in_size, int* start, int* val_size, int* end)
|
||||
{
|
||||
int i=0;
|
||||
|
||||
*start = in_size; //default no value
|
||||
*end = in_size; //default end
|
||||
*val_size=0; //default no value
|
||||
|
||||
while(i<in_size)
|
||||
{
|
||||
if(i+1<in_size && in[i] == '/' && in[i+1] == '/')
|
||||
while(i<in_size && in[i] != '\n')
|
||||
i++;
|
||||
|
||||
if(_isRead(in[i]))
|
||||
break;
|
||||
|
||||
i++;
|
||||
}
|
||||
if(i >= in_size) //ends without value
|
||||
return "";
|
||||
|
||||
int j=i; //name start
|
||||
while(i<in_size && in[i] != '=') //skip to =
|
||||
i++;
|
||||
if(i >= in_size) //no =
|
||||
throw format_error("Tag has no value", "", std::string(in, in_size), j);
|
||||
|
||||
if(i == j) //nothing preceding =
|
||||
throw format_error("Value has no tag", "", std::string(in, in_size), i);
|
||||
|
||||
int k=i-1; //name end
|
||||
while( !_isRead(in[k]) )
|
||||
k--;
|
||||
std::string name=std::string(in+j, k-j+1);
|
||||
|
||||
i++;
|
||||
while(i < in_size && !_isRead(in[i]))
|
||||
i++;
|
||||
if(i >= in_size) //no value
|
||||
{
|
||||
*start=i;
|
||||
*val_size=0;
|
||||
*end=i;
|
||||
return name;
|
||||
}
|
||||
if(in[i] == '\"') //"" val
|
||||
{
|
||||
i++;
|
||||
*start=i; //value starts
|
||||
j=0; //size
|
||||
while(i+j < in_size && in[i+j]!='\"')
|
||||
{
|
||||
if(in[i]+j=='\\')
|
||||
j++;
|
||||
j++;
|
||||
}
|
||||
if(i+j >= in_size) // no closing "
|
||||
throw format_error("Double quote does not close", "", std::string(in, in_size), i-1);
|
||||
*val_size=j;
|
||||
*end=i+j+1;
|
||||
return name;
|
||||
}
|
||||
if(in[i] == '\'') //"" val
|
||||
{
|
||||
i++;
|
||||
*start=i; //value starts
|
||||
j=0; //size
|
||||
while(i+j < in_size && in[i+j]!='\'')
|
||||
{
|
||||
if(in[i]+j=='\\')
|
||||
j++;
|
||||
j++;
|
||||
}
|
||||
if(i+j >= in_size) // no closing '
|
||||
throw format_error("Single quote does not close", "", std::string(in, in_size), i-1);
|
||||
*val_size=j;
|
||||
*end=i+j+1;
|
||||
return name;
|
||||
}
|
||||
if(in[i] == '{')
|
||||
{
|
||||
*start=i;
|
||||
j=1;
|
||||
int counter=0;
|
||||
while( i+j < in_size && !( counter == 0 && in[i+j]=='}') )
|
||||
{
|
||||
if(i+j+1<in_size && in[i+j] == '/' && in[i+j+1] == '/')
|
||||
while(i+j<in_size && in[i+j] != '\n')
|
||||
j++;
|
||||
if(in[i+j]=='\\')
|
||||
j++;
|
||||
if(in[i+j]=='{')
|
||||
counter++;
|
||||
if(in[i+j]=='}')
|
||||
counter--;
|
||||
j++;
|
||||
}
|
||||
if(i+j >= in_size) //reached end without closing
|
||||
throw format_error("Brace does not close", "", std::string(in, in_size), i);
|
||||
j++;
|
||||
*val_size=j;
|
||||
*end=i+j;
|
||||
return name;
|
||||
}
|
||||
if(in[i] == '[')
|
||||
{
|
||||
*start=i;
|
||||
j=1;
|
||||
int counter=0;
|
||||
while( i+j < in_size && !( counter == 0 && in[i+j]==']') )
|
||||
{
|
||||
if(i+j+1<in_size && in[i+j] == '/' && in[i+j+1] == '/')
|
||||
while(i+j<in_size && in[i+j] != '\n')
|
||||
j++;
|
||||
if(in[i+j]=='\\')
|
||||
j++;
|
||||
if(in[i+j]=='[')
|
||||
counter++;
|
||||
if(in[i+j]==']')
|
||||
counter--;
|
||||
j++;
|
||||
}
|
||||
if(i+j >= in_size) //reached end without closing
|
||||
throw format_error("Bracket does not close", "", std::string(in, in_size), i);
|
||||
j++;
|
||||
*val_size=j;
|
||||
*end=i+j;
|
||||
return name;
|
||||
}
|
||||
{ // no encapsulation: go to end of line
|
||||
*start=i; //value starts
|
||||
j=0; //size
|
||||
while(i+j < in_size && in[i+j]!='\n')
|
||||
{
|
||||
if(in[i]+j=='\\')
|
||||
j++;
|
||||
j++;
|
||||
}
|
||||
while( !_isRead(in[i+j]) )
|
||||
j--;
|
||||
*val_size=j+1;
|
||||
*end=i+j+1;
|
||||
return name;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string _getlist(const char* in, const int in_size, int* start, int* end)
|
||||
{
|
||||
int i=0;
|
||||
std::string ret;
|
||||
|
||||
while(i<in_size)
|
||||
{
|
||||
if(i+1<in_size && in[i] == '/' && in[i+1] == '/')
|
||||
while(i<in_size && in[i] != '\n')
|
||||
i++;
|
||||
|
||||
if(_isRead(in[i]))
|
||||
break;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
*start=i;
|
||||
if(i >= in_size) //ends without value
|
||||
{
|
||||
*end = in_size;
|
||||
return "";
|
||||
}
|
||||
if(in[i] == ',') //value is empty
|
||||
{
|
||||
*end=i+1;
|
||||
return "";
|
||||
}
|
||||
|
||||
int j=0;
|
||||
if(in[i] == '\"') //"" val
|
||||
{
|
||||
i++;
|
||||
j=0; //size
|
||||
while(i+j < in_size && in[i+j]!='\"')
|
||||
{
|
||||
if(in[i]+j=='\\')
|
||||
j++;
|
||||
j++;
|
||||
}
|
||||
if(i+j >= in_size) // no closing "
|
||||
throw format_error("Double quote does not close", "", std::string(in, in_size), i-1);
|
||||
ret = std::string(in+i, j);
|
||||
*end=i+j+1;
|
||||
}
|
||||
else if(in[i] == '\'') //"" val
|
||||
{
|
||||
i++;
|
||||
j=0; //size
|
||||
while(i+j < in_size && in[i+j]!='\'')
|
||||
{
|
||||
if(in[i]+j=='\\')
|
||||
j++;
|
||||
j++;
|
||||
}
|
||||
if(i+j >= in_size) // no closing '
|
||||
throw format_error("Single quote does not close", "", std::string(in, in_size), i-1);
|
||||
ret = std::string(in+i, j);
|
||||
*end=i+j+1;
|
||||
}
|
||||
else if(in[i] == '{')
|
||||
{
|
||||
j=1;
|
||||
int counter=0;
|
||||
while( i+j < in_size && !( counter == 0 && in[i+j]=='}') )
|
||||
{
|
||||
if(i+j+1<in_size && in[i+j] == '/' && in[i+j+1] == '/')
|
||||
while(i+j<in_size && in[i+j] != '\n')
|
||||
j++;
|
||||
if(in[i+j]=='\\')
|
||||
j++;
|
||||
if(in[i+j]=='{')
|
||||
counter++;
|
||||
if(in[i+j]=='}')
|
||||
counter--;
|
||||
j++;
|
||||
}
|
||||
if(i+j >= in_size) //reached end without closing
|
||||
throw format_error("Brace does not close", "", std::string(in, in_size), i);
|
||||
j++;
|
||||
ret = std::string(in+i, j);
|
||||
*end=i+j;
|
||||
}
|
||||
else if(in[i] == '[')
|
||||
{
|
||||
j=1;
|
||||
int counter=0;
|
||||
while( i+j < in_size && !( counter == 0 && in[i+j]==']') )
|
||||
{
|
||||
if(i+j+1<in_size && in[i+j] == '/' && in[i+j+1] == '/')
|
||||
while(i+j<in_size && in[i+j] != '\n')
|
||||
j++;
|
||||
if(in[i+j]=='\\')
|
||||
j++;
|
||||
if(in[i+j]=='[')
|
||||
counter++;
|
||||
if(in[i+j]==']')
|
||||
counter--;
|
||||
j++;
|
||||
}
|
||||
if(i+j >= in_size) //reached end without closing
|
||||
throw format_error("Bracket does not close", "", std::string(in, in_size), i);
|
||||
j++;
|
||||
ret = std::string(in+i, j);
|
||||
*end=i+j;
|
||||
}
|
||||
else // no encapsulation: go to next ,
|
||||
{
|
||||
j=0; //size
|
||||
while(i+j < in_size && in[i+j]!=',')
|
||||
{
|
||||
if(in[i+j]=='\\')
|
||||
j++;
|
||||
j++;
|
||||
}
|
||||
if(i+j < in_size)
|
||||
{
|
||||
while( !_isRead(in[i+j]) )
|
||||
j--;
|
||||
}
|
||||
ret = std::string(in+i,j);
|
||||
*end=i+j;
|
||||
}
|
||||
|
||||
i = *end;
|
||||
while(i < in_size && !_isRead(in[i]))
|
||||
i++;
|
||||
if( i>= in_size ) //last char
|
||||
{
|
||||
*end=i;
|
||||
return ret;
|
||||
}
|
||||
else if(in[i] ==',') //comma as expected
|
||||
{
|
||||
*end=i+1;
|
||||
return ret;
|
||||
}
|
||||
else //Unexpected char
|
||||
throw format_error("Expecting comma", "", std::string(in, in_size), i);
|
||||
|
||||
}
|
||||
|
||||
void Chunk::set(const char* in, const int in_size, int offset, Filedat* data)
|
||||
{
|
||||
this->clear(); //reset everything
|
||||
this->m_parent=data;
|
||||
this->m_offset=offset;
|
||||
|
||||
int i=0;
|
||||
|
||||
while(i<in_size && !_isRead(in[i])) //skip unread char
|
||||
i++;
|
||||
|
||||
if(i >= in_size) //empty: make an empty strval
|
||||
{
|
||||
DataVal* cv = new DataVal();
|
||||
m_achunk=cv;
|
||||
cv->val = "";
|
||||
return;
|
||||
}
|
||||
else if( in[i] == '{')
|
||||
{
|
||||
i++;
|
||||
int val_end=in_size-1;
|
||||
while(!_isRead(in[val_end])) //skip unread char
|
||||
val_end--;
|
||||
if(in[val_end] != '}')
|
||||
throw format_error("Expecting closing brace", "", std::string(in, in_size), val_end+1);
|
||||
|
||||
DataChunk* tch = new DataChunk();
|
||||
m_achunk = tch;
|
||||
|
||||
std::string name;
|
||||
std::string val;
|
||||
while(i < val_end)
|
||||
{
|
||||
int start=0;
|
||||
int _size=0;
|
||||
int end=0;
|
||||
|
||||
while(!_isRead(in[i]))
|
||||
i++;
|
||||
|
||||
std::string newstr=std::string(in+i, val_end-i);
|
||||
try
|
||||
{
|
||||
name = _getname(newstr.c_str(), newstr.size(), &start, &_size, &end);
|
||||
val = newstr.substr(start, _size);
|
||||
}
|
||||
catch(format_error& e)
|
||||
{
|
||||
throw format_error(e.what(), "", std::string(in, in_size), e.where()+i);
|
||||
}
|
||||
|
||||
if( name == "" ) //no more values
|
||||
break;
|
||||
|
||||
try
|
||||
{
|
||||
Chunk* chk = new Chunk(val.c_str(),val.size(), offset + start+i, m_parent);
|
||||
if(!tch->values.insert( std::make_pair(name, chk ) ).second)
|
||||
{
|
||||
delete chk;
|
||||
throw format_error("Key '" + name + "' already present", "", std::string(in, in_size), 0 - start );
|
||||
}
|
||||
}
|
||||
catch(format_error& e)
|
||||
{
|
||||
throw format_error(e.what(), "", std::string(in, in_size), e.where() + start + i );
|
||||
}
|
||||
|
||||
i += end;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
else if( in[i] == '[')
|
||||
{
|
||||
i++;
|
||||
int val_end=in_size-1;
|
||||
while(!_isRead(in[val_end])) //skip unread char
|
||||
val_end--;
|
||||
if(in[val_end] != ']')
|
||||
throw format_error("Expecting closing bracket", "", std::string(in, in_size), val_end+1);
|
||||
|
||||
ChunkList* tch = new ChunkList();
|
||||
m_achunk = tch;
|
||||
|
||||
int end=0,start=0;
|
||||
while( i < val_end )
|
||||
{
|
||||
std::string val;
|
||||
std::string newstr=std::string(in+i, val_end-i);
|
||||
try
|
||||
{
|
||||
val = _getlist(newstr.c_str(), newstr.size(), &start, &end);
|
||||
}
|
||||
catch(format_error& e)
|
||||
{
|
||||
throw format_error(e.what(), "", std::string(in, in_size), e.where()+i);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
tch->list.push_back(new Chunk(val.c_str(),val.size(), offset + start+i, m_parent) );
|
||||
}
|
||||
catch(format_error& e)
|
||||
{
|
||||
throw format_error(e.what(), "", std::string(in, in_size), e.where() + start + i );
|
||||
}
|
||||
|
||||
i+=end;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
else // string value
|
||||
{
|
||||
int val_end=in_size;
|
||||
val_end--;
|
||||
while(!_isRead(in[val_end])) //skip unread char
|
||||
val_end--;
|
||||
|
||||
DataVal* tch = new DataVal();
|
||||
m_achunk = tch;
|
||||
|
||||
tch->val = std::string(in+i,val_end-i+1);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Chunk::addToChunk(std::string const& name, Chunk const& val)
|
||||
{
|
||||
if(this->type()==AbstractChunk::chunk)
|
||||
{
|
||||
DataChunk* cp = dynamic_cast<DataChunk*>(m_achunk);
|
||||
Chunk* chk = new Chunk(val);
|
||||
if( !cp->values.insert( std::make_pair(name,chk) ).second )
|
||||
{
|
||||
delete chk;
|
||||
throw format_error("Key '" + name + "' already present", "", this->strval(), -1);
|
||||
}
|
||||
}
|
||||
else if(this->type() == AbstractChunk::none)
|
||||
{
|
||||
DataChunk* cp = new DataChunk();
|
||||
m_achunk=cp;
|
||||
cp->values.insert(std::make_pair(name , new Chunk(val)));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw format_error("Cannot add keys to non-map chunks", "", this->strval(), -1);
|
||||
}
|
||||
}
|
||||
|
||||
void Chunk::addToChunk(std::vector<std::pair<std::string, Chunk>> const& vec)
|
||||
{
|
||||
for(auto it : vec)
|
||||
this->addToChunk(it.first, it.second);
|
||||
}
|
||||
|
||||
void Chunk::addToList(Chunk const& val)
|
||||
{
|
||||
if(this->type()==AbstractChunk::list)
|
||||
{
|
||||
ChunkList* lp = dynamic_cast<ChunkList*>(m_achunk);
|
||||
lp->list.push_back(new Chunk(val));
|
||||
}
|
||||
else if(this->type() == AbstractChunk::none)
|
||||
{
|
||||
ChunkList* lp = new ChunkList();
|
||||
m_achunk=lp;
|
||||
lp->list.push_back(new Chunk(val));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw format_error("Cannot add elements to non-list chunks", "", this->strval(), -1);
|
||||
}
|
||||
}
|
||||
|
||||
void Chunk::addToList(std::vector<Chunk> const& vec)
|
||||
{
|
||||
for(auto it : vec)
|
||||
this->addToList(it);
|
||||
}
|
||||
|
||||
std::string Chunk::strval(unsigned int alignment, std::string const& aligner) const
|
||||
{
|
||||
if(this->type()==AbstractChunk::val)
|
||||
{
|
||||
DataVal* vp = dynamic_cast<DataVal*>(m_achunk);
|
||||
return vp->val;
|
||||
}
|
||||
else if(this->type()==AbstractChunk::chunk)
|
||||
{
|
||||
DataChunk* cp = dynamic_cast<DataChunk*>(m_achunk);
|
||||
std::string ret="{\n";
|
||||
for(auto it : cp->values)
|
||||
{
|
||||
ret += _repeatString(aligner,alignment+1);
|
||||
ret += it.first;
|
||||
ret += '=';
|
||||
if(it.second!=nullptr)
|
||||
ret += it.second->strval(alignment+1, aligner);
|
||||
ret += '\n';
|
||||
}
|
||||
ret += _repeatString(aligner, alignment);
|
||||
ret += '}';
|
||||
return ret;
|
||||
}
|
||||
else if(this->type()==AbstractChunk::list)
|
||||
{
|
||||
ChunkList* lp = dynamic_cast<ChunkList*>(m_achunk);
|
||||
std::string ret="[\n";
|
||||
for(auto it : lp->list)
|
||||
{
|
||||
ret += _repeatString(aligner, alignment+1);
|
||||
if(it!=nullptr)
|
||||
ret += it->strval(alignment+1, aligner);
|
||||
ret += ",\n";
|
||||
}
|
||||
ret.erase(ret.end()-2);
|
||||
ret += _repeatString(aligner, alignment);
|
||||
ret += ']';
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
int Chunk::listSize() const
|
||||
{
|
||||
if(this->type() != AbstractChunk::list)
|
||||
return -1;
|
||||
ChunkList* cl = dynamic_cast<ChunkList*>(m_achunk);
|
||||
return cl->list.size();
|
||||
}
|
||||
|
||||
Chunk* Chunk::subChunkPtr(std::string const& in) const
|
||||
{
|
||||
if(this->type()==AbstractChunk::chunk)
|
||||
{
|
||||
DataChunk* dc = dynamic_cast<DataChunk*>(m_achunk);
|
||||
auto fi = dc->values.find(in);
|
||||
if(fi == dc->values.end()) //none found
|
||||
return nullptr;
|
||||
return fi->second;
|
||||
}
|
||||
else //not a chunk
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Chunk* Chunk::subChunkPtr(unsigned int a) const
|
||||
{
|
||||
if(this->type()==AbstractChunk::list)
|
||||
{
|
||||
ChunkList* cl = dynamic_cast<ChunkList*>(m_achunk);
|
||||
if(a >= cl->list.size()) //outside of range
|
||||
return nullptr;
|
||||
return cl->list[a];
|
||||
}
|
||||
else //not a list
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Chunk& Chunk::subChunkRef(std::string const& in) const
|
||||
{
|
||||
if(this->type()!=AbstractChunk::chunk)
|
||||
{
|
||||
if(m_parent != nullptr)
|
||||
{
|
||||
throw format_error("Chunk isn't a map", m_parent->filePath(), m_parent->stringdata(), m_offset );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw format_error("Chunk isn't a map", "", this->strval(), -1);
|
||||
}
|
||||
}
|
||||
DataChunk* dc = dynamic_cast<DataChunk*>(m_achunk);
|
||||
auto fi = dc->values.find(in);
|
||||
if(fi == dc->values.end())
|
||||
{
|
||||
if(m_parent != nullptr)
|
||||
{
|
||||
throw format_error("Map doesn't have '" + in + "' flag", m_parent->filePath(), m_parent->stringdata(), m_offset );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw format_error("Map doesn't have '" + in + "' flag", "", this->strval(), -1);
|
||||
}
|
||||
}
|
||||
return *fi->second;
|
||||
}
|
||||
|
||||
Chunk& Chunk::subChunkRef(unsigned int a) const
|
||||
{
|
||||
if(this->type()!=AbstractChunk::list)
|
||||
{
|
||||
if(m_parent != nullptr)
|
||||
{
|
||||
throw format_error("Chunk isn't a list", m_parent->filePath(), m_parent->stringdata(), m_offset );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw format_error("Chunk isn't a list", "", this->strval(), -1);
|
||||
}
|
||||
}
|
||||
ChunkList* cl = dynamic_cast<ChunkList*>(m_achunk);
|
||||
if(a >= cl->list.size())
|
||||
{
|
||||
if(m_parent != nullptr)
|
||||
{
|
||||
throw format_error("List size is below " + std::to_string(a), m_parent->filePath(), m_parent->stringdata(), m_offset );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw format_error("List size is below " + std::to_string(a), "", this->strval(), -1);
|
||||
}
|
||||
}
|
||||
return *cl->list[a];
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, Chunk const& a)
|
||||
{
|
||||
stream << a.strval();
|
||||
return stream;
|
||||
}
|
||||
|
||||
DataChunk::~DataChunk()
|
||||
{
|
||||
for(auto it : values)
|
||||
{
|
||||
if(it.second != nullptr)
|
||||
delete it.second;
|
||||
}
|
||||
}
|
||||
|
||||
ChunkList::~ChunkList()
|
||||
{
|
||||
for(auto it : list)
|
||||
{
|
||||
if(it!=nullptr)
|
||||
delete it;
|
||||
}
|
||||
}
|
||||
136
src/device.cpp
136
src/device.cpp
|
|
@ -3,19 +3,42 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <ztd/shell.hpp>
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
std::vector<Device*> device_list;
|
||||
|
||||
static bool _isNum(char a)
|
||||
static std::string dequote(const std::string& in)
|
||||
{
|
||||
return (a>='0' && a<='9');
|
||||
std::string ret;
|
||||
const char quote = in[0];
|
||||
if(quote != '\'' && quote !='\"')
|
||||
return in;
|
||||
unsigned int i=1;
|
||||
while(i<in.size())
|
||||
{
|
||||
if(in[i] == quote) {
|
||||
break;
|
||||
}
|
||||
else if(i+1 < in.size() && in[i] == '\\' && in[i+1] == quote) {
|
||||
i+=2;
|
||||
ret += quote;
|
||||
}
|
||||
else{
|
||||
ret += in[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sh(std::string const& string)
|
||||
static bool _isNum(const char& a)
|
||||
{
|
||||
system(string.c_str());
|
||||
return (a>='0' && a<='9');
|
||||
}
|
||||
|
||||
Device::Device()
|
||||
|
|
@ -23,6 +46,7 @@ Device::Device()
|
|||
busy=false;
|
||||
nb_command=0;
|
||||
client_id=-1;
|
||||
thread_pid=-1;
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
|
|
@ -35,17 +59,16 @@ bool Device::start_loop()
|
|||
if(this->busy)
|
||||
return false;
|
||||
this->busy = true;
|
||||
this->thread = std::thread(Device::loop, this);
|
||||
this->thread.detach();
|
||||
std::thread(Device::loop, this).detach();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<int32_t,int32_t> importRange(Chunk const& ch, std::string const& tag, int32_t low, int32_t high)
|
||||
std::pair<int32_t,int32_t> importRange(const ztd::chunkdat& ch, const std::string& tag, int32_t low, int32_t high)
|
||||
{
|
||||
Chunk* pch=ch.subChunkPtr(tag);
|
||||
ztd::chunkdat* pch=ch.subChunkPtr(tag);
|
||||
if(pch != nullptr)
|
||||
{
|
||||
std::string str=pch->strval();
|
||||
std::string str=*pch;
|
||||
auto tpos=str.find(':');
|
||||
if (str=="*") //whole range
|
||||
{
|
||||
|
|
@ -53,25 +76,25 @@ std::pair<int32_t,int32_t> importRange(Chunk const& ch, std::string const& tag,
|
|||
}
|
||||
else if(tpos == std::string::npos) //single value
|
||||
{
|
||||
low=stoi(str);
|
||||
low=std::stoi(str);
|
||||
high=low;
|
||||
}
|
||||
else //range
|
||||
{
|
||||
low=stoi(str.substr(0,tpos));
|
||||
low=std::stoi(str.substr(0,tpos));
|
||||
tpos++;
|
||||
high=stoi(str.substr(tpos, str.size()-tpos));
|
||||
high=std::stoi(str.substr(tpos, str.size()-tpos));
|
||||
}
|
||||
}
|
||||
return std::make_pair(low, high);
|
||||
}
|
||||
|
||||
std::pair<float,float> importRangeFloat(Chunk const& ch, std::string const& tag, float low, float high)
|
||||
std::pair<float,float> importRangeFloat(const ztd::chunkdat& ch, const std::string& tag, float low, float high)
|
||||
{
|
||||
Chunk* pch=ch.subChunkPtr(tag);
|
||||
ztd::chunkdat* pch=ch.subChunkPtr(tag);
|
||||
if(pch != nullptr)
|
||||
{
|
||||
std::string str=pch->strval();
|
||||
std::string str=*pch;
|
||||
auto tpos=str.find(':');
|
||||
if(tpos == std::string::npos)
|
||||
{
|
||||
|
|
@ -90,12 +113,12 @@ std::pair<float,float> importRangeFloat(Chunk const& ch, std::string const& tag,
|
|||
return std::make_pair(low, high);
|
||||
}
|
||||
|
||||
bool importBool(Chunk const& ch, std::string const& tag, bool defbool)
|
||||
bool importBool(const ztd::chunkdat& ch, const std::string& tag, const bool& defbool)
|
||||
{
|
||||
Chunk* pch=ch.subChunkPtr(tag);
|
||||
ztd::chunkdat* pch=ch.subChunkPtr(tag);
|
||||
if(pch != nullptr)
|
||||
{
|
||||
std::string str=pch->strval();
|
||||
std::string str=*pch;
|
||||
if( str == "true" )
|
||||
return true;
|
||||
else if( str == "false" )
|
||||
|
|
@ -104,30 +127,30 @@ bool importBool(Chunk const& ch, std::string const& tag, bool defbool)
|
|||
return defbool;
|
||||
}
|
||||
|
||||
bool Device::import_chunk(Chunk const& ch)
|
||||
bool Device::import_chunk(const ztd::chunkdat& ch)
|
||||
{
|
||||
Chunk& cch = ch["commands"];
|
||||
this->name=ch["name"].strval();
|
||||
ztd::chunkdat& cch = ch["commands"];
|
||||
this->name=dequote(ch["name"]);
|
||||
for(int i=0 ; i<cch.listSize() ; i++)
|
||||
{
|
||||
Chunk& tch=cch[i];
|
||||
std::string tstr=tch["type"].strval();
|
||||
ztd::chunkdat& tch=cch[i];
|
||||
std::string tstr=tch["type"];
|
||||
if(tstr == "system") //type system
|
||||
{
|
||||
std::string shell;
|
||||
shell=tch["shell"].strval();
|
||||
shell=tch["shell"];
|
||||
this->sysCommands.push_back(SystemCommand(shell));
|
||||
}
|
||||
else if (tstr == "connect")
|
||||
{
|
||||
std::string shell;
|
||||
shell=tch["shell"].strval();
|
||||
shell=tch["shell"];
|
||||
this->connectCommands.push_back(ConnectCommand(shell));
|
||||
}
|
||||
else if (tstr == "disconnect")
|
||||
{
|
||||
std::string shell;
|
||||
shell=tch["shell"].strval();
|
||||
shell=tch["shell"];
|
||||
this->disconnectCommands.push_back(DisconnectCommand(shell));
|
||||
}
|
||||
else
|
||||
|
|
@ -139,13 +162,13 @@ bool Device::import_chunk(Chunk const& ch)
|
|||
std::pair<float,float> floatpair;
|
||||
|
||||
//channel
|
||||
if(tch.subChunkPtr("channel") == nullptr || tch["channel"].strval()=="*")
|
||||
if(tch.subChunkPtr("channel") == nullptr || tch["channel"] == "*")
|
||||
channel=-1;
|
||||
else
|
||||
channel=stoi(tch["channel"].strval());
|
||||
channel=std::stoi(tch["channel"]);
|
||||
|
||||
//shell
|
||||
shell=tch["shell"].strval();
|
||||
shell=tch["shell"];
|
||||
|
||||
//type
|
||||
if(tstr == "note") //type note
|
||||
|
|
@ -154,13 +177,11 @@ bool Device::import_chunk(Chunk const& ch)
|
|||
uint8_t id_high=127;
|
||||
uint8_t low=1;
|
||||
uint8_t high=127;
|
||||
std::string tt;
|
||||
|
||||
//id
|
||||
intpair=importRange(tch, "id", id_low, id_high);
|
||||
id_low=intpair.first;
|
||||
id_high=intpair.second;
|
||||
// id=stoi(tch["id"].strval());
|
||||
|
||||
//trigger
|
||||
intpair = importRange(tch, "trigger", low, high);
|
||||
|
|
@ -171,7 +192,6 @@ bool Device::import_chunk(Chunk const& ch)
|
|||
{
|
||||
this->noteCommands[i].push_back(NoteCommand(channel,low,high,shell));
|
||||
}
|
||||
this->nb_command++;
|
||||
}
|
||||
else if(tstr == "controller") //type controller
|
||||
{
|
||||
|
|
@ -205,7 +225,6 @@ bool Device::import_chunk(Chunk const& ch)
|
|||
{
|
||||
this->ctrlCommands[i].push_back(ControllerCommand(channel,min,max,mapMin,mapMax,floating,shell));
|
||||
}
|
||||
this->nb_command++;
|
||||
}
|
||||
else if(tstr == "pitch") //type pitch bend
|
||||
{
|
||||
|
|
@ -229,7 +248,6 @@ bool Device::import_chunk(Chunk const& ch)
|
|||
floating = importBool(tch, "float", floating);
|
||||
|
||||
this->pitchCommands.push_back(PitchCommand(channel,min,max,mapMin,mapMax,floating,shell));
|
||||
this->nb_command++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -237,6 +255,7 @@ bool Device::import_chunk(Chunk const& ch)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
this->nb_command++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -245,8 +264,7 @@ void Device::run_signal(char* buff)
|
|||
{
|
||||
if ( (strstr(buff, "Port unsubscribed") != NULL) ) // distonnected
|
||||
{
|
||||
std::string kill_command=KILL_COMMAND_FH + std::to_string(this->client_id) + KILL_COMMAND_SH;
|
||||
system(kill_command.c_str()); // kill the process
|
||||
kill(this->thread_pid, SIGINT);
|
||||
this->busy=false;
|
||||
this->client_id=-1;
|
||||
}
|
||||
|
|
@ -267,8 +285,8 @@ void Device::run_signal(char* buff)
|
|||
for( auto it : this->sysCommands )
|
||||
{
|
||||
std::string command="code=" + strval + ";";
|
||||
command += it.shell;
|
||||
std::thread(sh, command).detach();
|
||||
command += dequote(it.shell);
|
||||
std::thread(ztd::shr, command).detach();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -276,7 +294,7 @@ void Device::run_signal(char* buff)
|
|||
int t;
|
||||
char type;
|
||||
int8_t channel;
|
||||
int ctid;
|
||||
int8_t ctid=-1;
|
||||
int16_t value;
|
||||
char* pos=NULL;
|
||||
bool note_off=false;
|
||||
|
|
@ -295,7 +313,7 @@ void Device::run_signal(char* buff)
|
|||
else
|
||||
{
|
||||
// throw std::runtime_error("Unknown MIDI signal:\n" + std::string(buff));
|
||||
printf("Unsupported signal, ignoring\n");
|
||||
std::cerr << "Unsupported signal, ignoring\n" ;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -344,8 +362,8 @@ void Device::run_signal(char* buff)
|
|||
std::string command="id=" + std::to_string(ctid)
|
||||
+ ";channel=" + std::to_string(channel)
|
||||
+ ";velocity=" + std::to_string(value) + ";";
|
||||
command += it.shell;
|
||||
std::thread(sh, command).detach();
|
||||
command += dequote(it.shell);
|
||||
std::thread(ztd::shr, command).detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -371,8 +389,8 @@ void Device::run_signal(char* buff)
|
|||
command += std::to_string(result);
|
||||
else
|
||||
command += std::to_string((long int) result);
|
||||
command += ";" + it.shell;
|
||||
std::thread(sh, command).detach();
|
||||
command += ";" + dequote(it.shell);
|
||||
std::thread(ztd::shr, command).detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -397,8 +415,8 @@ void Device::run_signal(char* buff)
|
|||
command += std::to_string(result);
|
||||
else
|
||||
command += std::to_string((long int) result);
|
||||
command += ";" + it.shell;
|
||||
std::thread(sh, command).detach();
|
||||
command += ";" + dequote(it.shell);
|
||||
std::thread(ztd::shr, command).detach();
|
||||
}
|
||||
}
|
||||
} // if type
|
||||
|
|
@ -411,14 +429,16 @@ void Device::run_signal(char* buff)
|
|||
|
||||
void Device::loop(Device* dev)
|
||||
{
|
||||
std::string command = "aseqdump -p '" + std::to_string(dev->client_id) + '\'';
|
||||
FILE *stream = popen(command.c_str(), "r");
|
||||
char* buff = NULL;
|
||||
size_t buff_size = 0;
|
||||
std::string command = "aseqdump -p '" + std::to_string(dev->client_id) + '\'';
|
||||
FILE *stream = ztd::popen2(command.c_str(), "r", &dev->thread_pid);
|
||||
|
||||
log("Device '" + dev->name + "' connected\n");
|
||||
|
||||
for( auto it : dev->connectCommands )
|
||||
{
|
||||
std::thread(sh, it.shell).detach();
|
||||
std::thread(ztd::shr, dequote(it.shell)).detach();
|
||||
}
|
||||
|
||||
while (getline(&buff, &buff_size, stream) > 0)
|
||||
|
|
@ -428,11 +448,21 @@ void Device::loop(Device* dev)
|
|||
|
||||
for( auto it : dev->disconnectCommands )
|
||||
{
|
||||
std::thread(sh, it.shell).detach();
|
||||
std::thread(ztd::shr, dequote(it.shell)).detach();
|
||||
}
|
||||
|
||||
printf("Device '%s' disconnected\n", dev->name.c_str());
|
||||
log("Device '" + dev->name + "' disconnected\n");
|
||||
|
||||
pclose(stream);
|
||||
ztd::pclose2(stream, dev->thread_pid);
|
||||
dev->thread_pid=-1;
|
||||
free(buff);
|
||||
}
|
||||
|
||||
void clean_devices()
|
||||
{
|
||||
for(auto it : device_list)
|
||||
{
|
||||
delete it;
|
||||
}
|
||||
device_list.clear();
|
||||
}
|
||||
|
|
|
|||
129
src/format.cpp
Normal file
129
src/format.cpp
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#include "format.hpp"
|
||||
|
||||
static void _skipline(const std::string& mim, unsigned int& i)
|
||||
{
|
||||
while(i<mim.size() && mim[i] != '\n') {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static void _skipread(const std::string& mim, unsigned int& i)
|
||||
{
|
||||
while(i<mim.size())
|
||||
{
|
||||
if(i+1<mim.size() && mim[i] == '/' && mim[i+1] == '/') {
|
||||
_skipline(mim, i);
|
||||
}
|
||||
if(ztd::filedat::isRead(mim[i])) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static void _find_next(const std::string& mim, unsigned int& i, const std::string& str)
|
||||
{
|
||||
while(i<mim.size())
|
||||
{
|
||||
_skipread(mim, i);
|
||||
if(i+str.size() < mim.size() && mim.substr(i, str.size()) == str)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
_skipline(mim, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string file_strimport(const std::string& path)
|
||||
{
|
||||
std::ifstream file(path);
|
||||
std::string ret, line;
|
||||
while(file)
|
||||
{
|
||||
getline(file, line);
|
||||
ret += line + '\n';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ztd::chunkdat mimtochk_commands(const std::string& mim)
|
||||
{
|
||||
ztd::chunkdat chk;
|
||||
unsigned int i=0,j=0;
|
||||
_find_next(mim,i, "Command ");
|
||||
while(i<mim.size())
|
||||
{
|
||||
i+=7;
|
||||
j=i;
|
||||
_skipline(mim,j);
|
||||
std::string type=mim.substr(i,j-i);
|
||||
j++;
|
||||
i=j;
|
||||
_find_next(mim,j, "Command ");
|
||||
std::string flags=mim.substr(i,j-i);
|
||||
i=j;
|
||||
|
||||
chk.add("{type=" + type + '\n' + flags + '}');
|
||||
}
|
||||
return chk;
|
||||
}
|
||||
|
||||
ztd::chunkdat mimtochk(std::string mim)
|
||||
{
|
||||
mim = ztd::filedat::removeComments(mim);
|
||||
ztd::chunkdat chk;
|
||||
unsigned int i=0,j=0;
|
||||
_find_next(mim,i, "Device ");
|
||||
while(i<mim.size())
|
||||
{
|
||||
ztd::chunkdat device;
|
||||
i+=7;
|
||||
j=i;
|
||||
_skipline(mim,j);
|
||||
std::string name=mim.substr(i,j-i);
|
||||
j++;
|
||||
i=j;
|
||||
_find_next(mim,j, "Device ");
|
||||
std::string commands=mim.substr(i,j-i);
|
||||
i=j;
|
||||
|
||||
device.add("name", name);
|
||||
device.add("commands", mimtochk_commands(commands));
|
||||
|
||||
chk.add(device);
|
||||
}
|
||||
return chk;
|
||||
}
|
||||
|
||||
std::string chktomim(ztd::chunkdat const& chk, std::string const& aligner)
|
||||
{
|
||||
std::string ret;
|
||||
for(int i=0 ; i<chk.listSize() ; i++)
|
||||
{
|
||||
ztd::chunkdat& device=chk[i];
|
||||
ret += "Device " + device.subChunkRef("name").str() + "\n\n";
|
||||
for(int j=0 ; j < device["commands"].listSize() ; j++)
|
||||
{
|
||||
ztd::chunkdat command=device["commands"][j];
|
||||
ret += aligner + "Command " + command["type"].str() + '\n';
|
||||
command.erase("type");
|
||||
for(auto it : command.getmap())
|
||||
{
|
||||
ret += aligner+aligner + it.first + '=' + it.second->str() + '\n';
|
||||
}
|
||||
ret += '\n';
|
||||
// ret += command.str(2, aligner) + '\n';
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool is_mim(const std::string& str)
|
||||
{
|
||||
unsigned int i=0;
|
||||
_skipread(str, i);
|
||||
return str.substr(i,7) == "Device ";
|
||||
}
|
||||
11
src/log.cpp
Normal file
11
src/log.cpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#include "log.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
bool log_on=DEFAULT_LOG_STATE;
|
||||
|
||||
void log(const std::string& str)
|
||||
{
|
||||
if(log_on)
|
||||
printf("%s", str.c_str());
|
||||
}
|
||||
331
src/main.cpp
331
src/main.cpp
|
|
@ -1,41 +1,53 @@
|
|||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "device.hpp"
|
||||
#include "system.hpp"
|
||||
|
||||
#include "Filedat.hpp"
|
||||
#include "options.hpp"
|
||||
|
||||
|
||||
#include "log.hpp"
|
||||
#include "help.h"
|
||||
|
||||
OptionSet options;
|
||||
#include "format.hpp"
|
||||
|
||||
#include <ztd/filedat.hpp>
|
||||
#include <ztd/options.hpp>
|
||||
#include <ztd/shell.hpp>
|
||||
|
||||
#define VERSION_STRING "v1.3.1a"
|
||||
|
||||
ztd::option_set options;
|
||||
|
||||
void help()
|
||||
{
|
||||
printf("zmidimap [options] <midimap file>\n\nOptions:\n");
|
||||
options.printHelp(2, 25);
|
||||
printf("\nSee --file-format --command-tags --shell-format options for details on map file format\n");
|
||||
printf("zmidimap [options] <file>\n\nOptions:\n");
|
||||
options.print_help(4, 25);
|
||||
printf("\n");
|
||||
printf("See --mim-format --zfd-format --command-tags --shell-format options for details on map file format\n");
|
||||
}
|
||||
|
||||
void version()
|
||||
{
|
||||
printf("zmidimap %s\n", VERSION_STRING);
|
||||
}
|
||||
|
||||
void option_p(const std::string& port)
|
||||
{
|
||||
std::string command="aseqdump -p '" + port + '\'';
|
||||
FILE *stream = popen(command.c_str(), "r");
|
||||
pid_t pid;
|
||||
FILE *stream = ztd::popen2(command.c_str(), "r", &pid);
|
||||
char* buff = NULL;
|
||||
size_t buff_size = 0;
|
||||
while (getline(&buff, &buff_size, stream) > 0)
|
||||
{
|
||||
if ( (strstr(buff, "Port unsubscribed") != NULL) ) // distonnected
|
||||
{
|
||||
std::string kill_command=KILL_COMMAND_FH + port + KILL_COMMAND_SH;
|
||||
system(kill_command.c_str()); // kill the process
|
||||
kill(pid, SIGINT); // kill the process
|
||||
}
|
||||
else
|
||||
printf("%s", buff);
|
||||
}
|
||||
ztd::pclose2(stream, pid);
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
|
|
@ -46,6 +58,7 @@ void cleanup()
|
|||
|
||||
void stop(int ret)
|
||||
{
|
||||
kill_all();
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
|
|
@ -54,120 +67,276 @@ void inthandler(int dummy)
|
|||
stop(0);
|
||||
}
|
||||
|
||||
void load_filedat(ztd::filedat& file, bool from_stdin, std::string const& path)
|
||||
{
|
||||
if(from_stdin)
|
||||
{
|
||||
log("Loading map from stdin\n");
|
||||
file.setFilePath(path);
|
||||
std::string str=file_strimport(path);
|
||||
|
||||
if(options["zfd"])
|
||||
{
|
||||
file.data() = str;
|
||||
}
|
||||
else if(options["mim"])
|
||||
{
|
||||
file.data() = mimtochk(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(is_mim(str))
|
||||
{
|
||||
file.data() = mimtochk(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
file.data() = str;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
file.setFilePath(path);
|
||||
log("Loading map file '" + path + "'\n");
|
||||
if(options["zfd"])
|
||||
{
|
||||
file.import_file();
|
||||
}
|
||||
else if(options["mim"])
|
||||
{
|
||||
file.data() = mimtochk(file_strimport(path));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string filestr=file_strimport(path);
|
||||
if(is_mim(filestr))
|
||||
{
|
||||
file.data() = mimtochk(filestr);
|
||||
}
|
||||
else
|
||||
{
|
||||
file.import_file();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void load_commands(ztd::chunkdat const& data)
|
||||
{
|
||||
clean_devices();
|
||||
for(int i=0 ; i<data.listSize() ; i++)
|
||||
{
|
||||
Device *newDevice = new Device;
|
||||
newDevice->import_chunk(data[i]);
|
||||
device_list.push_back(newDevice);
|
||||
log("Loaded "+std::to_string(newDevice->nb_command)+" commands for device '"+newDevice->name+"'\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
signal(SIGINT, inthandler);
|
||||
signal(SIGCHLD, SIG_IGN); //not expecting returns from child processes
|
||||
|
||||
options.addOption(Option('h',"help", false, "Display this help message"));
|
||||
options.addOption(Option("file-format", false, "Display file format help"));
|
||||
options.addOption(Option("command-tags", false, "Display for command tag help"));
|
||||
options.addOption(Option("shell-format", false, "Display for shell format help"));
|
||||
options.addOption(Option('l',"list", false, "List detected devices"));
|
||||
options.addOption(Option('L',"full-list", false, "Print whole device list details"));
|
||||
options.addOption(Option('p',"port", true, "Connect to device and output to console", "device"));
|
||||
// options.addOption(Option('i',"interactive", false, "Start in interactive mode"));
|
||||
bool autoreload=true;
|
||||
bool from_stdin=false;
|
||||
|
||||
auto argvec = argVector(argc, argv);
|
||||
options.add(
|
||||
ztd::option("\r [Help]"),
|
||||
ztd::option('h',"help", false, "Display this help message"),
|
||||
ztd::option('v',"version", false, "Display version"),
|
||||
ztd::option("mim-format", false, "Display mim file format help"),
|
||||
ztd::option("zfd-format", false, "Display zfd file format help"),
|
||||
ztd::option("command-tags", false, "Display for command tag help"),
|
||||
ztd::option("shell-format", false, "Display for shell format help"),
|
||||
ztd::option("\r [Format]"),
|
||||
ztd::option("no-log", false, "Disable console logging"),
|
||||
ztd::option("\r [Devices]"),
|
||||
ztd::option('l',"list", false, "List detected devices"),
|
||||
ztd::option('L',"full-list", false, "Print whole device list details"),
|
||||
ztd::option('p',"port", true, "Connect to device and output to console", "device"),
|
||||
ztd::option("\r [Map file]"),
|
||||
ztd::option('m',"mim", false, "Read file in mim format"),
|
||||
ztd::option('z',"zfd", false, "Read file in zfd format"),
|
||||
ztd::option('o',"output", true, "Output the resulting map to file", "file"),
|
||||
ztd::option("out-zfd", false, "Output in zfd format"),
|
||||
ztd::option("aligner", true, "String to use for aligning output map format. Default \\t", "string"),
|
||||
ztd::option("no-reload", false, "Disable auto reloading when file changes are detected")
|
||||
);
|
||||
|
||||
auto t = options.getOptions(argvec);
|
||||
std::vector<std::string> arg=t.first;
|
||||
if( !t.second ) //invalid option
|
||||
return 1;
|
||||
std::vector<std::string> arg;
|
||||
try
|
||||
{
|
||||
arg = options.process(argc, argv);
|
||||
}
|
||||
catch(ztd::option_error& err)
|
||||
{
|
||||
printf("%s\n", err.what());
|
||||
if(err.type() == ztd::option_error::unknown_option)
|
||||
help();
|
||||
stop(1);
|
||||
}
|
||||
|
||||
Option* op=nullptr;
|
||||
op = options.findOption('h');
|
||||
if( op->activated )
|
||||
//exit options
|
||||
if( options['h'] )
|
||||
{
|
||||
help();
|
||||
return 0;
|
||||
stop(0);
|
||||
}
|
||||
op = options.findOption("file-format");
|
||||
if( op->activated )
|
||||
if( options['v'] )
|
||||
{
|
||||
printf("%s\n", FILE_FORMAT);
|
||||
return 0;
|
||||
version();
|
||||
stop(0);
|
||||
}
|
||||
op = options.findOption("command-tags");
|
||||
if( op->activated )
|
||||
if( options["mim-format"] )
|
||||
{
|
||||
printf("%s\n", MIM_FORMAT);
|
||||
stop(0);
|
||||
}
|
||||
if( options["zfd-format"] )
|
||||
{
|
||||
printf("%s\n", ZFD_FORMAT);
|
||||
stop(0);
|
||||
}
|
||||
if( options["command-tags"] )
|
||||
{
|
||||
printf("%s\n", COMMAND_TAGS);
|
||||
return 0;
|
||||
stop(0);
|
||||
}
|
||||
op = options.findOption("shell-format");
|
||||
if( op->activated )
|
||||
if( options["shell-format"] )
|
||||
{
|
||||
printf("%s\n", SHELL_FORMAT);
|
||||
return 0;
|
||||
stop(0);
|
||||
}
|
||||
op = options.findOption('h');
|
||||
if( op->activated )
|
||||
if( options['L'] )
|
||||
{
|
||||
help();
|
||||
return 0;
|
||||
ztd::shr("aseqdump -l");
|
||||
stop(0);
|
||||
}
|
||||
op = options.findOption('L');
|
||||
if( op->activated )
|
||||
if( options['l'] )
|
||||
{
|
||||
sh("aseqdump -l");
|
||||
return 0;
|
||||
}
|
||||
op = options.findOption('l');
|
||||
if( op->activated )
|
||||
{
|
||||
sh(LIST_COMMAND);
|
||||
return 0;
|
||||
}
|
||||
op = options.findOption('p');
|
||||
if( op->activated )
|
||||
{
|
||||
option_p(op->argument);
|
||||
return 0;
|
||||
ztd::shr(LIST_COMMAND);
|
||||
stop(0);
|
||||
}
|
||||
|
||||
if( options['p'] )
|
||||
{
|
||||
option_p(options['p']);
|
||||
stop(0);
|
||||
}
|
||||
|
||||
if (options['o'])
|
||||
{
|
||||
log_on=false;
|
||||
}
|
||||
|
||||
//behavioral options
|
||||
if( options["no-reload"] )
|
||||
{
|
||||
autoreload=false;
|
||||
}
|
||||
if( options["no-log"] )
|
||||
{
|
||||
log_on=false;
|
||||
}
|
||||
std::string aligner="\t";
|
||||
if(options["aligner"])
|
||||
{
|
||||
aligner=options["aligner"].argument;
|
||||
}
|
||||
|
||||
//no argument: display help
|
||||
ztd::filedat file;
|
||||
std::string filepath;
|
||||
if (arg.size() <= 0 || arg[0] == "")
|
||||
{
|
||||
help();
|
||||
return 0;
|
||||
stop(0);
|
||||
}
|
||||
|
||||
Filedat file(arg[0]);
|
||||
if (!file.readTest())
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "File '%s' unavailable\n", arg[0].c_str());
|
||||
return 10;
|
||||
filepath=arg[0];
|
||||
}
|
||||
if(filepath == "-")
|
||||
{
|
||||
filepath = "/dev/stdin";
|
||||
from_stdin = true;
|
||||
}
|
||||
|
||||
printf("Loading map file '%s'\n", arg[0].c_str());
|
||||
//main processing
|
||||
try
|
||||
{
|
||||
file.import_file();
|
||||
|
||||
//create commands
|
||||
for(int i=0 ; i<file.chunk().listSize() ; i++)
|
||||
//load
|
||||
load_filedat(file, from_stdin, filepath);
|
||||
//output
|
||||
if(options['o'])
|
||||
{
|
||||
Device *newDevice = new Device;
|
||||
newDevice->import_chunk(file[i]);
|
||||
device_list.push_back(newDevice);
|
||||
printf("Loaded %d commands for device '%s'\n", newDevice->nb_command, newDevice->name.c_str());
|
||||
std::string ret;
|
||||
if(options["out-zfd"])
|
||||
ret=file.data().str(0, aligner);
|
||||
else
|
||||
ret=chktomim(file.data(), aligner);
|
||||
if(options['o'].argument == "-") {
|
||||
std::cout << ret << std::endl;
|
||||
}
|
||||
else {
|
||||
std::ofstream output(options['o']);
|
||||
if(!output)
|
||||
{
|
||||
std::cerr << "Cannot write to file '" + options['o'].argument + "'\n";
|
||||
return 1;
|
||||
}
|
||||
output << ret << std::endl ;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//create commands
|
||||
load_commands(file.data());
|
||||
|
||||
//main loop
|
||||
printf("Starting scan for devices\n");
|
||||
announce_loop();
|
||||
log("Starting scan for devices\n");
|
||||
if(autoreload)
|
||||
std::thread(filetime_loop, filepath).detach(); // start the killer thread
|
||||
announce_loop(); // loop until killed
|
||||
ztd::chunkdat bak_data = file.data();
|
||||
while(autoreload)
|
||||
{
|
||||
log("Reloading file\n");
|
||||
try
|
||||
{
|
||||
load_filedat(file, from_stdin, filepath);
|
||||
load_commands(file.data());
|
||||
bak_data = file.data();
|
||||
}
|
||||
catch (ztd::format_error& e)
|
||||
{
|
||||
ztd::printFormatException(e);
|
||||
log("Reloading old config\n");
|
||||
load_commands(bak_data);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
log("Reloading old config\n");
|
||||
load_commands(bak_data);
|
||||
}
|
||||
announce_loop(); // loop until killed
|
||||
}
|
||||
}
|
||||
catch (format_error& e)
|
||||
catch (ztd::format_error& e)
|
||||
{
|
||||
printFormatException(e);
|
||||
ztd::printFormatException(e);
|
||||
cleanup();
|
||||
stop(11);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
stop(-1);
|
||||
stop(1);
|
||||
}
|
||||
|
||||
cleanup();
|
||||
return 0;
|
||||
stop(0);
|
||||
}
|
||||
|
|
|
|||
235
src/options.cpp
235
src/options.cpp
|
|
@ -1,235 +0,0 @@
|
|||
#include "options.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
std::vector<std::string> argVector(int argc, char** argv)
|
||||
{
|
||||
std::vector<std::string> out;
|
||||
for(int i=1;i<argc; i++)
|
||||
{
|
||||
out.push_back(std::string(argv[i]));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Option::Option()
|
||||
{
|
||||
shortDef=false;
|
||||
longDef=false;
|
||||
takesArgument=false;
|
||||
activated=false;
|
||||
charName=0;
|
||||
}
|
||||
Option::~Option()
|
||||
{
|
||||
}
|
||||
|
||||
Option::Option(char c, bool arg, std::string helptext, std::string argname)
|
||||
{
|
||||
shortDef=true;
|
||||
longDef=false;
|
||||
takesArgument=arg;
|
||||
activated=false;
|
||||
charName=c;
|
||||
arg_name=argname;
|
||||
help_text=helptext;
|
||||
}
|
||||
|
||||
Option::Option(std::string const& str, bool arg, std::string helptext, std::string argname)
|
||||
{
|
||||
shortDef=false;
|
||||
longDef=true;
|
||||
takesArgument=arg;
|
||||
activated=false;
|
||||
charName=0;
|
||||
strName=str;
|
||||
arg_name=argname;
|
||||
help_text=helptext;
|
||||
}
|
||||
Option::Option(char c, std::string const& str, bool arg, std::string helptext, std::string argname)
|
||||
{
|
||||
shortDef=true;
|
||||
longDef=true;
|
||||
takesArgument=arg;
|
||||
activated=false;
|
||||
charName=c;
|
||||
strName=str;
|
||||
arg_name=argname;
|
||||
help_text=helptext;
|
||||
}
|
||||
|
||||
void Option::printHelp(int leftpad, int rightpad)
|
||||
{
|
||||
//prepadding
|
||||
printf("%*s", -1*leftpad, "");
|
||||
|
||||
//short def
|
||||
if(this->shortDef)
|
||||
{
|
||||
printf("-%c ", this->charName);
|
||||
rightpad -= 3;
|
||||
}
|
||||
|
||||
//longdef
|
||||
if(this->longDef)
|
||||
{
|
||||
printf("--%s ", this->strName.c_str());
|
||||
rightpad -= 3 + this->strName.size();
|
||||
}
|
||||
|
||||
//argument
|
||||
if(this->takesArgument)
|
||||
{
|
||||
printf(" <%s>", arg_name.c_str());
|
||||
rightpad -= arg_name.size() + 3;
|
||||
}
|
||||
|
||||
printf("%*s%s", -1*rightpad, "", help_text.c_str());
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void OptionSet::printHelp(int leftpad, int rightpad)
|
||||
{
|
||||
for(auto it : this->m_options)
|
||||
{
|
||||
it.printHelp(leftpad,rightpad);
|
||||
}
|
||||
}
|
||||
|
||||
OptionSet::OptionSet()
|
||||
{
|
||||
errStream = &(std::cerr);
|
||||
}
|
||||
|
||||
OptionSet::~OptionSet()
|
||||
{
|
||||
}
|
||||
|
||||
Option* OptionSet::findOption(char c)
|
||||
{
|
||||
for( auto it=m_options.begin() ; it!=m_options.end() ; it++ )
|
||||
{
|
||||
if((*it).shortDef && (*it).charName == c)
|
||||
return &(*it);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
Option* OptionSet::findOption(std::string const& str)
|
||||
{
|
||||
for( auto it=m_options.begin() ; it!=m_options.end() ; it++ )
|
||||
{
|
||||
if((*it).longDef && (*it).strName == str)
|
||||
return &(*it);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::pair<std::vector<std::string>, bool> OptionSet::getOptions(std::vector<std::string> input)
|
||||
{
|
||||
std::vector<std::string> out;
|
||||
unsigned int i=0;
|
||||
for( auto it = input.begin(); it!=input.end() ; it++ )
|
||||
{
|
||||
if( (*it).size()>0 && (*it)[0]=='-' )
|
||||
{
|
||||
if((*it).size()>1 && (*it)[1]=='-')
|
||||
{
|
||||
std::size_t eqn=(*it).find('=');
|
||||
if(eqn == std::string::npos)
|
||||
{
|
||||
Option* popt = this->findOption( (*it).substr( 2,(*it).size()-2) );
|
||||
if(popt == nullptr)
|
||||
{
|
||||
(*errStream) << "Unknown option: " << (*it).substr(0,eqn) << std::endl;
|
||||
return std::make_pair(out, false);
|
||||
}
|
||||
if(popt->takesArgument)
|
||||
{
|
||||
if( ++it == input.end() ) //finishes here
|
||||
{
|
||||
(*errStream) << "No argument given to option --" << popt->strName << std::endl;
|
||||
return std::make_pair(out, false);
|
||||
}
|
||||
popt->activated = true;
|
||||
popt->argument = (*it);
|
||||
}
|
||||
else
|
||||
{
|
||||
popt->activated = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Option* popt = this->findOption( (*it).substr(2,eqn-2) );
|
||||
if(popt == nullptr)
|
||||
{
|
||||
(*errStream) << "Unknown option: " << (*it).substr(2,eqn-2) << std::endl;
|
||||
return std::make_pair(out, false);
|
||||
}
|
||||
if(!popt->takesArgument)
|
||||
{
|
||||
(*errStream) << "Option " << popt->strName << " doesn't take an argument" << std::endl;
|
||||
return std::make_pair(out, false);
|
||||
}
|
||||
popt->argument = (*it).substr(eqn+1,(*it).size()-eqn-1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i=1;
|
||||
Option* popt=nullptr;
|
||||
bool tstop=false;
|
||||
while( !tstop && it!=input.end() && (*it).size()>i )
|
||||
{
|
||||
popt=this->findOption((*it)[i]);
|
||||
if(popt==nullptr) //not found: error
|
||||
{
|
||||
(*errStream) << "Unknown option: -" << (*it)[i] << std::endl;
|
||||
return std::make_pair(out, false);
|
||||
}
|
||||
if(popt->takesArgument) //no argument
|
||||
{
|
||||
i++;
|
||||
if((*it).size()<=i) //finishes here
|
||||
{
|
||||
if( ++it == input.end() )
|
||||
{
|
||||
(*errStream) << "No argument given to option -" << popt->charName << std::endl;
|
||||
return std::make_pair(out, false);
|
||||
}
|
||||
popt->activated = true;
|
||||
popt->argument = (*it);
|
||||
tstop = true;
|
||||
}
|
||||
else //continue
|
||||
{
|
||||
if( (*it)[i] != '=') //incorrect
|
||||
{
|
||||
(*errStream) << "No argument given to option -" << popt->charName << std::endl;
|
||||
return std::make_pair(out, false);
|
||||
}
|
||||
i++;
|
||||
popt->argument = (*it).substr(i , (*it).size()-i );
|
||||
popt->activated = true;
|
||||
tstop=true;
|
||||
}
|
||||
}
|
||||
else //no argument
|
||||
{
|
||||
popt->activated = true;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out.push_back(*it);
|
||||
}
|
||||
if(it == input.end())
|
||||
break;
|
||||
}
|
||||
return std::make_pair(out, true);
|
||||
}
|
||||
|
|
@ -1,20 +1,38 @@
|
|||
#include "system.hpp"
|
||||
|
||||
#include "device.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <ztd/shell.hpp>
|
||||
|
||||
#include "device.hpp"
|
||||
|
||||
int announce_thread_pid = 0;
|
||||
|
||||
void kill_all()
|
||||
{
|
||||
if(announce_thread_pid>0)
|
||||
kill(announce_thread_pid, SIGINT);
|
||||
for(auto it : device_list)
|
||||
{
|
||||
if(it->thread_pid>0)
|
||||
kill(it->thread_pid, SIGINT);
|
||||
}
|
||||
}
|
||||
|
||||
void device_check()
|
||||
{
|
||||
char* buff = NULL;
|
||||
size_t buff_size = 0;
|
||||
FILE *stream = popen(LIST_EXTENDED_COMMAND, "r");
|
||||
std::string str;
|
||||
std::vector<std::pair<int,std::string>> ls_device;
|
||||
|
||||
getline(&buff, &buff_size, stream); //discard the first line
|
||||
|
|
@ -37,9 +55,10 @@ void device_check()
|
|||
i++;
|
||||
while(buff[i-1] == ' ')
|
||||
i--;
|
||||
|
||||
//insert element
|
||||
ls_device.push_back(std::make_pair(t, std::string(buff+j, i-j)));
|
||||
}
|
||||
pclose(stream);
|
||||
|
||||
for ( auto dev : device_list ) // iterate devices
|
||||
{
|
||||
|
|
@ -48,7 +67,6 @@ void device_check()
|
|||
if( !dev->busy && dev->name == ls->second && ls->first > 0) //device detected
|
||||
{
|
||||
dev->client_id = ls->first;
|
||||
printf("Device '%s' found\n", dev->name.c_str());
|
||||
dev->start_loop();
|
||||
}
|
||||
if( dev->busy && dev->client_id == ls->first )
|
||||
|
|
@ -64,7 +82,7 @@ void announce_loop()
|
|||
{
|
||||
char* buff = NULL;
|
||||
size_t buff_size = 0;
|
||||
FILE *stream = popen(ANNOUNCE_COMMAND,"r");
|
||||
FILE* stream = ztd::popen2(ANNOUNCE_COMMAND, "r", &announce_thread_pid);
|
||||
|
||||
if (stream == NULL)
|
||||
{
|
||||
|
|
@ -78,6 +96,25 @@ void announce_loop()
|
|||
device_check();
|
||||
}
|
||||
|
||||
ztd::pclose2(stream, announce_thread_pid);
|
||||
|
||||
if(buff != NULL)
|
||||
free(buff);
|
||||
}
|
||||
|
||||
void filetime_loop(std::string const& filepath)
|
||||
{
|
||||
struct stat attrib;
|
||||
stat(filepath.c_str(), &attrib);
|
||||
time_t last_time=attrib.st_ctime;
|
||||
while(true)
|
||||
{
|
||||
stat(filepath.c_str(), &attrib);
|
||||
if(attrib.st_ctime > last_time)
|
||||
{
|
||||
kill_all();
|
||||
last_time = attrib.st_ctime;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue