Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
[gnokii.git] / common / phones / atgen.c
1 /*
2
3   $Id$
4
5   G N O K I I
6
7   A Linux/Unix toolset and driver for mobile phones.
8
9   Copyright 2001 Manfred Jonsson <manfred.jonsson@gmx.de>
10
11   Released under the terms of the GNU GPL, see file COPYING for more details.
12
13   This file provides functions specific to generic at command compatible
14   phones. See README for more details on supported mobile phones.
15
16   $Log$
17   Revision 1.1.1.1.12.1  2001/11/27 21:35:51  short
18   Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
19
20   Revision 1.1.1.1.8.1  2001/11/27 21:30:44  short
21   Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
22
23   Revision 1.1.1.1.2.1  2001/11/27 04:37:59  short
24   Update: orig2001_11_25_22_56 -> orig2001_11_27_05_17
25
26   Revision 1.1.1.2  2001/11/27 04:19:30  short
27   :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Tue Nov 27 05:17 CET 2001
28
29   Revision 1.7  2001/11/26 18:06:08  pkot
30   Checking for *printf functions, N_(x) for localization, generic ARRAY_LEN, SAFE_STRNCPY, G_GNUC_PRINTF (Jan Kratochvil)
31
32   Revision 1.6  2001/11/19 13:03:18  pkot
33   nk3110.c cleanup
34
35   Revision 1.5  2001/11/08 16:49:19  pkot
36   Cleanups
37
38   Revision 1.4  2001/08/20 23:36:27  pkot
39   More cleanup in AT code (Manfred Jonsson)
40
41   Revision 1.3  2001/08/20 23:27:37  pkot
42   Add hardware shakehand to the link layer (Manfred Jonsson)
43
44   Revision 1.2  2001/08/09 11:51:39  pkot
45   Generic AT support updates and cleanup (Manfred Jonsson)
46
47   Revision 1.1  2001/07/27 00:02:21  pkot
48   Generic AT support for the new structure (Manfred Jonsson)
49
50 */
51
52 #include <string.h>
53 #include <stdlib.h>
54 #include <ctype.h>
55
56 #include "misc.h"
57 #include "gsm-common.h"
58 #include "gsm-statemachine.h"
59 #include "gsm-encoding.h"
60 #include "phones/generic.h"
61 #include "phones/atgen.h"
62 #include "phones/ateric.h"
63 #include "phones/atsie.h"
64 #include "phones/atnok.h"
65 #include "links/atbus.h"
66 #include "links/cbus.h"
67
68
69 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state);
70 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state);
71 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
72 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
73 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
74 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
75 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
76 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data);
77
78 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state);
79 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state);
80 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state);
81 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state);
82 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state);
83 static GSM_Error AT_GetBattery(GSM_Data *data,  GSM_Statemachine *state);
84 static GSM_Error AT_GetRFLevel(GSM_Data *data,  GSM_Statemachine *state);
85 static GSM_Error AT_GetMemoryStatus(GSM_Data *data,  GSM_Statemachine *state);
86 static GSM_Error AT_ReadPhonebook(GSM_Data *data,  GSM_Statemachine *state);
87
88 typedef struct {
89         int gop;
90         AT_SendFunctionType sfunc;
91         GSM_RecvFunctionType rfunc;
92 } AT_FunctionInitType;
93
94
95 /* Mobile phone information */
96 static AT_SendFunctionType AT_Functions[GOP_Max];
97 static GSM_IncomingFunctionType IncomingFunctions[GOP_Max];
98 static AT_FunctionInitType AT_FunctionInit[] = {
99         { GOP_Init, NULL, Reply },
100         { GOP_GetModel, AT_GetModel, ReplyIdentify },
101         { GOP_GetRevision, AT_GetRevision, ReplyIdentify },
102         { GOP_GetImei, AT_GetIMEI, ReplyIdentify },
103         { GOP_GetManufacturer, AT_GetManufacturer, ReplyIdentify },
104         { GOP_Identify, AT_Identify, ReplyIdentify },
105         { GOP_GetBatteryLevel, AT_GetBattery, ReplyGetBattery },
106         { GOP_GetPowersource, AT_GetBattery, ReplyGetBattery },
107         { GOP_GetRFLevel, AT_GetRFLevel, ReplyGetRFLevel },
108         { GOP_GetMemoryStatus, AT_GetMemoryStatus, ReplyMemoryStatus },
109         { GOP_ReadPhonebook, AT_ReadPhonebook, ReplyReadPhonebook },
110 };
111
112
113 #define REPLY_SIMPLETEXT(l1, l2, c, t) \
114         if ((0 == strcmp(l1, c)) && (NULL != t)) strcpy(t, l2)
115
116
117 GSM_Phone phone_at = {
118         IncomingFunctions,
119         PGEN_IncomingDefault,
120         {
121                 "AT|AT-HW|dancall",                     /* Supported models */
122                 99,                     /* Max RF Level */
123                 0,                      /* Min RF Level */
124                 GRF_CSQ,                /* RF level units */
125                 100,                    /* Max Battery Level */
126                 0,                      /* Min Battery Level */
127                 GBU_Percentage,         /* Battery level units */
128                 0,                      /* Have date/time support */
129                 0,                      /* Alarm supports time only */
130                 0,                      /* Alarms available - FIXME */
131                 0, 0,                   /* Startup logo size - FIXME */
132                 0, 0,                   /* Op logo size */
133                 0, 0                    /* Caller logo size */
134         },
135         Functions
136 };
137
138
139 static GSM_MemoryType memorytype = GMT_XX;
140 static int atcharset = 0;
141
142 static char *memorynames[] = {
143         "ME", /* Internal memory of the mobile equipment */
144         "SM", /* SIM card memory */
145         "FD", /* Fixed dial numbers */
146         "ON", /* Own numbers */
147         "EN", /* Emergency numbers */
148         "DC", /* Dialled numbers */
149         "RC", /* Received numbers */
150         "MC", /* Missed numbers */
151         "LD", /* Last dialed */
152         "MT", /* combined ME and SIM phonebook */
153         "TA", /* for compatibility only: TA=computer memory */
154         "CB", /* Currently selected memory */
155 };
156
157 /* LinkOK is always true for now... */
158 bool ATGEN_LinkOK = true;
159
160
161 GSM_RecvFunctionType AT_InsertRecvFunction(int type, GSM_RecvFunctionType func)
162 {
163         static int pos = 0;
164         int i;
165         GSM_RecvFunctionType oldfunc;
166
167         if (type >= GOP_Max) {
168                 return (GSM_RecvFunctionType) -1;
169         }
170         if (pos == 0) {
171                 IncomingFunctions[pos].MessageType = type;
172                 IncomingFunctions[pos].Functions = func;
173                 pos++;
174                 return NULL;
175         }
176         for (i=0; i < pos; i++) {
177                 if (IncomingFunctions[i].MessageType == type) {
178                         oldfunc = IncomingFunctions[i].Functions;
179                         IncomingFunctions[i].Functions = func;
180                         return oldfunc;
181                 }
182         }
183         if (pos < GOP_Max-1) {
184                 IncomingFunctions[pos].MessageType = type;
185                 IncomingFunctions[pos].Functions = func;
186                 pos++;
187         }
188         return NULL;
189 }
190
191
192 AT_SendFunctionType AT_InsertSendFunction(int type, AT_SendFunctionType func)
193 {
194         AT_SendFunctionType f;
195
196         f = AT_Functions[type];
197         AT_Functions[type] = func;
198         return f;
199 }
200
201
202 static GSM_Error SetEcho(GSM_Data *data, GSM_Statemachine *state)
203 {
204         char req[128];
205
206         sprintf(req, "ATE1\r\n");
207         if (SM_SendMessage(state, 6, GOP_Init, req) != GE_NONE) return GE_NOTREADY;
208         return SM_Block(state, data, GOP_Init);
209 }
210
211
212 GSM_Error AT_SetMemoryType(GSM_MemoryType mt, GSM_Statemachine *state)
213 {
214         char req[128];
215         GSM_Error ret = GE_NONE;
216         GSM_Data data;
217
218         if (mt != memorytype) {
219                 sprintf(req, "AT+CPBS=\"%s\"\r\n", memorynames[mt]);
220                 ret = SM_SendMessage(state, 14, GOP_Init, req);
221                 if (ret != GE_NONE)
222                         return GE_NOTREADY;
223                 GSM_DataClear(&data);
224                 ret = SM_Block(state, &data, GOP_Init);
225                 if (ret == GE_NONE)
226                         memorytype = mt;
227         }
228         return ret;
229 }
230
231
232 static GSM_Error SetCharset(GSM_Statemachine *state)
233 {
234         char req[128];
235         GSM_Error ret = GE_NONE;
236         GSM_Data data;
237
238         if (atcharset == 0) {
239                 sprintf(req, "AT+CSCS=\"GSM\"\r\n");
240                 ret = SM_SendMessage(state, 15, GOP_Init, req);
241                 if (ret != GE_NONE)
242                         return GE_NOTREADY;
243                 GSM_DataClear(&data);
244                 ret = SM_Block(state, &data, GOP_Init);
245                 if (ret == GE_NONE)
246                         atcharset = 1;
247         }
248         return ret;
249 }
250
251
252 static GSM_Error AT_Identify(GSM_Data *data, GSM_Statemachine *state)
253 {
254         GSM_Error ret;
255
256         if ((ret = Functions(GOP_GetModel, data, state)) != GE_NONE)
257                 return ret;
258         if ((ret = Functions(GOP_GetManufacturer, data, state)) != GE_NONE)
259                 return ret;
260         if ((ret = Functions(GOP_GetRevision, data, state)) != GE_NONE)
261                 return ret;
262         return Functions(GOP_GetImei, data, state);
263 }
264
265
266 static GSM_Error AT_GetModel(GSM_Data *data, GSM_Statemachine *state)
267 {
268         char req[128];
269
270         sprintf(req, "AT+CGMM\r\n");
271         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
272                 return GE_NOTREADY;
273         return SM_Block(state, data, GOP_Identify);
274 }
275
276
277 static GSM_Error AT_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
278 {
279         char req[128];
280
281         sprintf(req, "AT+CGMI\r\n");
282         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
283                 return GE_NOTREADY;
284         return SM_Block(state, data, GOP_Identify);
285 }
286
287
288 static GSM_Error AT_GetRevision(GSM_Data *data, GSM_Statemachine *state)
289 {
290         char req[128];
291
292         sprintf(req, "AT+CGMR\r\n");
293         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
294                 return GE_NOTREADY;
295         return SM_Block(state, data, GOP_Identify);
296 }
297
298
299 static GSM_Error AT_GetIMEI(GSM_Data *data, GSM_Statemachine *state)
300 {
301         char req[128];
302
303         sprintf(req, "AT+CGSN\r\n");
304         if (SM_SendMessage(state, 9, GOP_Identify, req) != GE_NONE)
305                 return GE_NOTREADY;
306         return SM_Block(state, data, GOP_Identify);
307 }
308
309
310 /* gets battery level and power source */
311
312 static GSM_Error AT_GetBattery(GSM_Data *data,  GSM_Statemachine *state)
313 {
314         char req[128];
315
316         sprintf(req, "AT+CBC\r\n");
317         if (SM_SendMessage(state, 8, GOP_GetBatteryLevel, req) != GE_NONE)
318                 return GE_NOTREADY;
319         return SM_Block(state, data, GOP_GetBatteryLevel);
320 }
321
322
323 static GSM_Error AT_GetRFLevel(GSM_Data *data,  GSM_Statemachine *state)
324 {
325         char req[128];
326  
327         sprintf(req, "AT+CSQ\r\n");
328         if (SM_SendMessage(state, 8, GOP_GetRFLevel, req) != GE_NONE)
329                 return GE_NOTREADY;
330         return SM_Block(state, data, GOP_GetRFLevel);
331 }
332  
333  
334 static GSM_Error AT_GetMemoryStatus(GSM_Data *data,  GSM_Statemachine *state)
335 {
336         char req[128];
337         GSM_Error ret;
338
339         ret = AT_SetMemoryType(data->MemoryStatus->MemoryType,  state);
340         if (ret != GE_NONE)
341                 return ret;
342         sprintf(req, "AT+CPBS?\r\n");
343         if (SM_SendMessage(state, 10, GOP_GetMemoryStatus, req) != GE_NONE)
344                 return GE_NOTREADY;
345         ret = SM_Block(state, data, GOP_GetMemoryStatus);
346         if (ret != GE_UNKNOWN)
347                 return ret;
348         sprintf(req, "AT+CPBR=?\r\n");
349         if (SM_SendMessage(state, 11, GOP_GetMemoryStatus, req) != GE_NONE)
350                 return GE_NOTREADY;
351         ret = SM_Block(state, data, GOP_GetMemoryStatus);
352         return ret;
353 }
354
355
356 static GSM_Error AT_ReadPhonebook(GSM_Data *data,  GSM_Statemachine *state)
357 {
358         char req[128];
359         GSM_Error ret;
360
361         ret = SetCharset(state);
362         if (ret != GE_NONE)
363                 return ret;
364         ret = AT_SetMemoryType(data->PhonebookEntry->MemoryType,  state);
365         if (ret != GE_NONE)
366                 return ret;
367         sprintf(req, "AT+CPBR=%d\r\n", data->PhonebookEntry->Location);
368         if (SM_SendMessage(state, strlen(req), GOP_ReadPhonebook, req) != GE_NONE)
369                 return GE_NOTREADY;
370         ret = SM_Block(state, data, GOP_ReadPhonebook);
371         return ret;
372 }
373
374
375 static GSM_Error Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
376 {
377         if (op == GOP_Init)
378                 return Initialise(data, state);
379         if ((op > GOP_Init) && (op < GOP_Max))
380                 if (AT_Functions[op])
381                         return (*AT_Functions[op])(data, state);
382         return GE_NOTIMPLEMENTED;
383 }
384
385
386 static GSM_Error ReplyReadPhonebook(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
387 {
388         AT_LineBuffer buf;
389         char *pos, *endpos;
390         int l;
391
392         buf.line1 = buffer;
393         buf.length= length;
394         splitlines(&buf);
395         if (buf.line1 == NULL)
396                 return GE_INVALIDPHBOOKLOCATION;
397
398         if (strncmp(buffer, "AT+CPBR", 7)) {
399                 return GE_NONE; /*FIXME*/
400         }
401
402         if (!strncmp(buf.line2, "OK", 2)) {
403                 if (data->PhonebookEntry) {
404                         *(data->PhonebookEntry->Number) = '\0';
405                         *(data->PhonebookEntry->Name) = '\0';
406                         data->PhonebookEntry->Group = 0;
407                         data->PhonebookEntry->SubEntriesCount = 0;
408                 }
409                 return GE_NONE;
410         }
411         if (data->PhonebookEntry) {
412                 data->PhonebookEntry->Group = 0;
413                 data->PhonebookEntry->SubEntriesCount = 0;
414                 pos = strchr(buf.line2, '\"');
415                 endpos = NULL;
416                 if (pos)
417                         endpos = strchr(++pos, '\"');
418                 if (endpos) {
419                         *endpos = '\0';
420                         strcpy(data->PhonebookEntry->Number, pos);
421                 }
422                 pos = NULL;
423                 if (endpos)
424                         pos = strchr(++endpos, '\"');
425                 endpos = NULL;
426                 if (pos) {
427                         pos++;
428                         l = pos - (char *)buffer;
429                         endpos = memchr(pos, '\"', length - l);
430                 }
431                 if (endpos) {
432                         l = endpos - pos;
433                         DecodeAscii(data->PhonebookEntry->Name, pos, l);
434                         *(data->PhonebookEntry->Name + l) = '\0';
435                 }
436         }
437         return GE_NONE;
438 }
439
440
441 static GSM_Error ReplyMemoryStatus(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
442 {
443         AT_LineBuffer buf;
444         char *pos;
445
446         buf.line1 = buffer;
447         buf.length= length;
448         splitlines(&buf);
449         if (buf.line1 == NULL)
450                 return GE_INVALIDMEMORYTYPE;
451
452         if (data->MemoryStatus) {
453                 if (strstr(buf.line2,"+CPBS")) {
454                         pos = strchr(buf.line2, ',');
455                         if (pos) {
456                                 data->MemoryStatus->Used = atoi(++pos);
457                         } else {
458                                 data->MemoryStatus->Used = 100;
459                                 data->MemoryStatus->Free = 0;
460                                 return GE_UNKNOWN;
461                         }
462                         pos = strchr(pos, ',');
463                         if (pos) {
464                                 data->MemoryStatus->Free = atoi(++pos) - data->MemoryStatus->Used;
465                         } else {
466                                 return GE_UNKNOWN;
467                         }
468                 }
469         }
470         return GE_NONE;
471 }
472
473
474 static GSM_Error ReplyGetBattery(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
475 {
476         AT_LineBuffer buf;
477         char *pos;
478
479         buf.line1 = buffer;
480         buf.length= length;
481         splitlines(&buf);
482         if ((buf.line1 == NULL) || (buf.line2 == NULL))
483                 return GE_NONE;
484
485         if (!strncmp(buffer, "AT+CBC", 6)) {
486                 if (data->BatteryLevel) {
487                         *(data->BatteryUnits) = GBU_Percentage;
488                         pos = strchr(buf.line2, ',');
489                         if (pos) {
490                                 pos++;
491                                 *(data->BatteryLevel) = atoi(pos);
492                         } else {
493                                 *(data->BatteryLevel) = 1;
494                         }
495                 }
496                 if (data->PowerSource) {
497                         *(data->PowerSource) = 0;
498                         if (*buf.line2 == '1') *(data->PowerSource) = GPS_ACDC;
499                         if (*buf.line2 == '0') *(data->PowerSource) = GPS_BATTERY;
500                 }
501         }
502         return GE_NONE;
503 }
504
505
506 static GSM_Error ReplyGetRFLevel(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
507 {
508         AT_LineBuffer buf;
509         char *pos1, *pos2;
510
511         buf.line1 = buffer;
512         buf.length= length;
513         splitlines(&buf);
514         if (buf.line1 == NULL)
515                 return GE_NONE;
516
517         if ((!strncmp(buffer, "AT+CSQ", 6)) && (data->RFUnits)) {
518                 *(data->RFUnits) = GRF_CSQ;
519                 pos1 = buf.line2 + 6;
520                 pos2 = strchr(buf.line2, ',');
521                 if (pos1 < pos2) {
522                         *(data->RFLevel) = atoi(pos1);
523                 } else {
524                         *(data->RFLevel) = 1;
525                 }
526         }
527         return GE_NONE;
528 }
529
530
531 static GSM_Error ReplyIdentify(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
532 {
533         AT_LineBuffer buf;
534
535         buf.line1 = buffer;
536         buf.length= length;
537         splitlines(&buf);
538         if (buf.line1 == NULL)
539                 return GE_NONE;         /* Fixme */
540         if (!strncmp(buffer, "AT+CG", 5)) {
541                 REPLY_SIMPLETEXT(buffer+5, buf.line2, "SN", data->Imei);
542                 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MM", data->Model);
543                 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MI", data->Manufacturer);
544                 REPLY_SIMPLETEXT(buffer+5, buf.line2, "MR", data->Revision);
545         }
546         return GE_NONE;
547 }
548
549
550 static GSM_Error Reply(int messagetype, unsigned char *buffer, int length, GSM_Data *data)
551 {
552         AT_LineBuffer buf;
553         int error = 0;
554
555         buf.line1 = buffer;
556         buf.length= length;
557         splitlines(&buf);
558         if (buf.line1 == NULL)
559                 error = 1;
560
561         return GE_NONE;
562 }
563
564
565 static GSM_Error Initialise(GSM_Data *setupdata, GSM_Statemachine *state)
566 {
567         GSM_Data data;
568         GSM_Error ret;
569         char model[20];
570         char manufacturer[20];
571         int i;
572
573         fprintf(stderr, "Initializing AT capable mobile phone ...\n");
574
575         /* Copy in the phone info */
576         memcpy(&(state->Phone), &phone_at, sizeof(GSM_Phone));
577
578         for (i=0; i<GOP_Max; i++) {
579                 AT_Functions[i] = NULL;
580                 IncomingFunctions[i].MessageType = 0;
581                 IncomingFunctions[i].Functions = NULL;
582         }
583         for (i=0; i<ARRAY_LEN(AT_FunctionInit); i++) {
584                 AT_InsertSendFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].sfunc);
585                 AT_InsertRecvFunction(AT_FunctionInit[i].gop, AT_FunctionInit[i].rfunc);
586         }
587
588         switch (state->Link.ConnectionType) {
589         case GCT_Serial:
590                 if (!strcmp(setupdata->Model, "dancall"))
591                         CBUS_Initialise(state);
592                 else if (!strcmp(setupdata->Model, "AT-HW"))
593                         ATBUS_Initialise(state, true);
594                 else
595                         ATBUS_Initialise(state, false);
596                 break;
597         default:
598                 return GE_NOTSUPPORTED;
599                 break;
600         }
601         SM_Initialise(state);
602
603         SetEcho(&data, state);
604
605         GSM_DataClear(&data);
606         data.Model = model;
607         ret = state->Phone.Functions(GOP_GetModel, &data, state);
608         if (ret != GE_NONE) return ret;
609         GSM_DataClear(&data);
610         data.Manufacturer = manufacturer;
611         ret = state->Phone.Functions(GOP_GetManufacturer, &data, state);
612         if (ret != GE_NONE) return ret;
613
614         if (!strncasecmp(manufacturer, "ericsson", 8))
615                 AT_InitEricsson(state, model, setupdata->Model);
616         if (!strncasecmp(manufacturer, "siemens", 7))
617                 AT_InitSiemens(state, model, setupdata->Model);
618         if (!strncasecmp(manufacturer, "nokia", 5))
619                 AT_InitNokia(state, model, setupdata->Model);
620
621         return GE_NONE;
622 }
623
624  
625 void splitlines(AT_LineBuffer *buf)
626 {
627         char *pos;
628
629         if ((buf->length > 7) && (!strncmp(buf->line1+buf->length-7, "ERROR", 5))) {
630                 buf->line1 = NULL;
631                 return;
632         }
633         pos = findcrlf(buf->line1, 0, buf->length);
634         if (pos) {
635                 *pos = 0;
636                 buf->line2 = skipcrlf(++pos);
637         } else {
638                 buf->line2 = buf->line1;
639         }
640         pos = findcrlf(buf->line2, 1, buf->length);
641         if (pos) {
642                 *pos = 0;
643                 buf->line3 = skipcrlf(++pos);
644         } else {
645                 buf->line3 = buf->line2;
646         }
647 }
648
649
650 /*
651  * increments the argument until a char unequal to
652  * <cr> or <lf> is found. returns the new position.
653  */
654  
655 char *skipcrlf(char *str)
656 {
657         if (str == NULL)
658                 return str;
659         while ((*str == '\n') || (*str == '\r'))
660                 str++;
661         return str;
662 }
663  
664  
665 /*
666  * searches for <cr> or <lf> and returns the first
667  * occurrence. if test is set, the gsm char @ which
668  * is 0x00 is not considered as end of string.
669  * return NULL if no <cr> or <lf> was found in the
670  * range of max bytes.
671  */
672  
673 char *findcrlf(char *str, int test, int max)
674 {
675         if (str == NULL)
676                 return str;
677         while ((*str != '\n') && (*str != '\r') && ((*str != '\0') || test) && (max > 0)) {
678                 str++;
679                 max--;
680         }
681         if ((*str == '\0') || ((max == 0) && (*str != '\n') && (*str != '\r')))
682                 return NULL;
683         return str;
684 }