jma_sp
I mostly use the interface to transfer binary data but when some of that stream carry's a string (like a rev string) then I simply move the bytes in to a string one at a time and let the string's internal code handle the memory allocation, length, & string termination.
repeat
str = str + char(Cdat[i]);
inc(i);
Until ( #0 = Cdat[i]) or (i >= bytecount) ;
As far as fixed return types, each AT command's response is different, some are ascii and some are binary. most follow the standard (see wiki) but some times a modem comes out that can do more than the standard so the manufacturer adds an extended command. that also means that as far as the AT modem commands go , the <eos> is a feature of the modem you are talking to. some are ok if you send CRLF, others only like one or the other {CR or LF}. I've work with one that required each (configuration) message start and end with either an esc char (#27) or ACK (#6). Best to check it's documentation.
As a receiver, you may find it simpler to just treat the input as binary at the synaser level and then sort/ separate the incoming stream in your software (byte by byte). that way you are in full control of when each string ends and the next begins (i.e old school).
PS:I'm not familiar with the AT+CGSM option. it may be the (phone's) interface is being defined above and beyond the modem level so you can follow one of the newer standards.