One way is to store the current and previous input states, and compare them every time that you poll the input.
For each key that can be pressed, store an object that has a timestamp of the last time that the key switched from a down state to an up state.
Update these objects by doing this at every poll:
void update(){
some_key_has_been_pressed = false;
foreach(key in keys){
if(previous_input[key].Down && current_input[key].Up){
keys[key].up_timestamp = current_time();
}
if(current_input[key].Down){
keys[key].down_timestamp = current_time();
some_key_has_been_pressed = true;
}
}
}
Now you can pattern match your combos against the contents of keys.
Have a combo object for each combo, and call update() on each combo object at each poll.
A combo object's update() method will pattern match the combo, checking if all necessary conditions for the combo are satisfied at this poll. I.e. all keys timestamps for the combo so far are in order, and no other key that would break the combo has been pressed this frame. For each condition met, increment a counter in the combo object to the next condition to check. When all conditions are met in a combo, call the method that the combo should perform.
If some_key_has_been_pressed == true but the key that is the next condition for the combo has not been pressed, then reset the combo's satisfied condition counter to 0.
The above is my preferred method, as it is simple to implement, easy to maintain, efficient, and highly modular.
However for another good method, check out the XNA input sequence sample, which is written in C#, and the logic is likely transferrable to the language you're using.