error inside information elements are now reported inside trace and main log,
[lcr.git] / interface.c
1 /*****************************************************************************\
2 **                                                                           **
3 ** LCR                                                                       **
4 **                                                                           **
5 **---------------------------------------------------------------------------**
6 ** Copyright: Andreas Eversberg                                              **
7 **                                                                           **
8 ** reading interface.conf file and filling structure                         **
9 **                                                                           **
10 \*****************************************************************************/ 
11
12 #include <stdio.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include "main.h"
16
17 struct interface *interface_first = NULL; /* first interface is current list */
18 struct interface *interface_newlist = NULL; /* first interface in new list */
19
20
21 /* set default out_channel */
22 void default_out_channel(struct interface_port *ifport)
23 {
24         struct select_channel *selchannel, **selchannelp;
25
26         selchannel = (struct select_channel *)MALLOC(sizeof(struct select_channel));
27         memuse++;
28         
29         if (ifport->mISDNport->ntmode)
30                 selchannel->channel = CHANNEL_FREE;
31         else
32                 selchannel->channel = CHANNEL_ANY;
33         
34         ifport->out_channel = selchannel;
35
36         /* additional channel selection for multipoint NT ports */
37         if (!ifport->mISDNport->ptp && ifport->mISDNport->ntmode)
38         {
39                 selchannelp = &(selchannel->next);
40                 selchannel = (struct select_channel *)MALLOC(sizeof(struct select_channel));
41                 memuse++;
42                 selchannel->channel = CHANNEL_NO; // call waiting
43                 *selchannelp = selchannel;
44         }
45 }
46
47
48 /* set default in_channel */
49 void default_in_channel(struct interface_port *ifport)
50 {
51         struct select_channel *selchannel;
52
53         selchannel = (struct select_channel *)MALLOC(sizeof(struct select_channel));
54         memuse++;
55         
56         selchannel->channel = CHANNEL_FREE;
57         
58         ifport->in_channel = selchannel;
59 }
60
61
62 /* parse string for a positive number */
63 static int get_number(char *value)
64 {
65         int val = 0;
66         char text[10];
67
68         val = atoi(value);
69         
70         SPRINT(text, "%d", val);
71
72         if (!strcmp(value, text))
73                 return(val);
74
75         return(-1);
76 }
77
78
79 /* remove element from buffer
80  * and return pointer to next element in buffer */
81 static char *get_seperated(char *buffer)
82 {
83         while(*buffer)
84         {
85                 if (*buffer==',' || *buffer<=32) /* seperate */
86                 {
87                         *buffer++ = '\0';
88                         while((*buffer>'\0' && *buffer<=32) || *buffer==',')
89                                 buffer++;
90                         return(buffer);
91                 }
92                 buffer++;
93         }
94         return(buffer);
95 }
96
97 /*
98  * parameter processing
99  */
100 static int inter_block(struct interface *interface, char *filename, int line, char *parameter, char *value)
101 {
102         struct interface_port *ifport;
103
104         /* port in chain ? */
105         if (!interface->ifport)
106         {
107                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
108                 return(-1);
109         }
110         /* goto end of chain */
111         ifport = interface->ifport;
112         while(ifport->next)
113                 ifport = ifport->next;
114         /* add value */
115         if (value[0])
116         {
117                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects no value.\n", filename, line, parameter);
118                 return(-1);
119         }
120         ifport->block = 1;
121         return(0);
122 }
123 static int inter_extension(struct interface *interface, char *filename, int line, char *parameter, char *value)
124 {
125         if (value[0])
126         {
127                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects no value.\n", filename, line, parameter);
128                 return(-1);
129         }
130         interface->extension = 1;
131         return(0);
132 }
133 static int inter_ptp(struct interface *interface, char *filename, int line, char *parameter, char *value)
134 {
135         struct interface_port *ifport;
136
137         /* port in chain ? */
138         if (!interface->ifport)
139         {
140                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
141                 return(-1);
142         }
143         if (interface->ifport->ptmp)
144         {
145                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' previously ptmp was given.\n", filename, line, parameter);
146                 return(-1);
147         }
148         /* goto end of chain */
149         ifport = interface->ifport;
150         while(ifport->next)
151                 ifport = ifport->next;
152         /* add value */
153         if (value[0])
154         {
155                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects no value.\n", filename, line, parameter);
156                 return(-1);
157         }
158         ifport->ptp = 1;
159         return(0);
160 }
161 static int inter_ptmp(struct interface *interface, char *filename, int line, char *parameter, char *value)
162 {
163         struct interface_port *ifport;
164
165         /* port in chain ? */
166         if (!interface->ifport)
167         {
168                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
169                 return(-1);
170         }
171         if (interface->ifport->ptp)
172         {
173                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' previously ptp was given.\n", filename, line, parameter);
174                 return(-1);
175         }
176         /* goto end of chain */
177         ifport = interface->ifport;
178         while(ifport->next)
179                 ifport = ifport->next;
180         /* add value */
181         if (value[0])
182         {
183                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects no value.\n", filename, line, parameter);
184                 return(-1);
185         }
186         ifport->ptmp = 1;
187         return(0);
188 }
189 static int inter_nt(struct interface *interface, char *filename, int line, char *parameter, char *value)
190 {
191 #ifdef SOCKET_MISDN
192         struct interface_port *ifport;
193
194         /* port in chain ? */
195         if (!interface->ifport)
196         {
197                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
198                 return(-1);
199         }
200         /* goto end of chain */
201         ifport = interface->ifport;
202         while(ifport->next)
203                 ifport = ifport->next;
204         /* add value */
205         if (value[0])
206         {
207                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects no value.\n", filename, line, parameter);
208                 return(-1);
209         }
210         ifport->nt = 1;
211 #endif
212         return(0);
213 }
214 static int inter_tones(struct interface *interface, char *filename, int line, char *parameter, char *value)
215 {
216         if (!strcasecmp(value, "yes"))
217         {
218                 interface->is_tones = IS_YES;
219         } else
220         if (!strcasecmp(value, "no"))
221         {
222                 interface->is_tones = IS_NO;
223         } else
224         {
225                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects value 'yes' or 'no'.\n", filename, line, parameter);
226                 return(-1);
227         }
228         return(0);
229 }
230 static int inter_earlyb(struct interface *interface, char *filename, int line, char *parameter, char *value)
231 {
232         if (!strcasecmp(value, "yes"))
233         {
234                 interface->is_earlyb = IS_YES;
235         } else
236         if (!strcasecmp(value, "no"))
237         {
238                 interface->is_earlyb = IS_NO;
239         } else
240         {
241                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects value 'yes' or 'no'.\n", filename, line, parameter);
242                 return(-1);
243         }
244         return(0);
245 }
246 static int inter_hunt(struct interface *interface, char *filename, int line, char *parameter, char *value)
247 {
248         if (!strcasecmp(value, "linear"))
249         {
250                 interface->hunt = HUNT_LINEAR;
251         } else
252         if (!strcasecmp(value, "roundrobin"))
253         {
254                 interface->hunt = HUNT_ROUNDROBIN;
255         } else
256         {
257                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects value 'linear' or 'roundrobin'.\n", filename, line, parameter);
258                 return(-1);
259         }
260         return(0);
261 }
262 static int inter_port(struct interface *interface, char *filename, int line, char *parameter, char *value)
263 {
264         struct interface_port *ifport, **ifportp;
265         struct interface *searchif;
266         int val;
267
268         val = get_number(value);
269         if (val == -1)
270         {
271                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects one numeric value.\n", filename, line, parameter);
272                 return(-1);
273         }
274         /* check for port already assigned */
275         searchif = interface_newlist;
276         while(searchif)
277         {
278                 ifport = searchif->ifport;
279                 while(ifport)
280                 {
281                         if (ifport->portnum == val)
282                         {
283                                 SPRINT(interface_error, "Error in %s (line %d): port '%d' already used above.\n", filename, line, val);
284                                 return(-1);
285                         }
286                         ifport = ifport->next;
287                 }
288                 searchif = searchif->next;
289         }
290         /* alloc port substructure */
291         ifport = (struct interface_port *)MALLOC(sizeof(struct interface_port));
292         memuse++;
293         ifport->interface = interface;
294         /* set value */
295         ifport->portnum = val;
296         /* tail port */
297         ifportp = &interface->ifport;
298         while(*ifportp)
299                 ifportp = &((*ifportp)->next);
300         *ifportp = ifport;
301         return(0);
302 }
303 static int inter_channel_out(struct interface *interface, char *filename, int line, char *parameter, char *value)
304 {
305         struct interface_port *ifport;
306         struct select_channel *selchannel, **selchannelp;
307         int val;
308         char *p, *el;
309
310         /* port in chain ? */
311         if (!interface->ifport)
312         {
313                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
314                 return(-1);
315         }
316         /* goto end of chain */
317         ifport = interface->ifport;
318         while(ifport->next)
319                 ifport = ifport->next;
320         p = value;
321         while(*p)
322         {
323                 el = p;
324                 p = get_seperated(p);
325                 if (!strcasecmp(el, "force"))
326                 {
327                         ifport->channel_force = 1;
328                         if (ifport->out_channel)
329                         {
330                                 SPRINT(interface_error, "Error in %s (line %d): value 'force' may only appear as first element in list.\n", filename, line);
331                                 return(-1);
332                         }
333                 } else
334                 if (!strcasecmp(el, "any"))
335                 {
336                         val = CHANNEL_ANY;
337                         goto selchannel;
338                 } else
339                 if (!strcasecmp(el, "free"))
340                 {
341                         val = CHANNEL_FREE;
342                         goto selchannel;
343                 } else
344                 if (!strcasecmp(el, "no"))
345                 {
346                         val = CHANNEL_NO;
347                         goto selchannel;
348                 } else
349                 {
350                         val = get_number(el);
351                         if (val == -1)
352                         {
353                                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects a comma seperated list of 'force', 'any', 'free', 'no' and any channel number.\n", filename, line, parameter);
354                                 return(-1);
355                         }
356
357                         if (val<1 || val==16 || val>126)
358                         {
359                                 SPRINT(interface_error, "Error in %s (line %d): channel '%d' out of range.\n", filename, line, val);
360                                 return(-1);
361                         }
362                         selchannel:
363                         /* add to select-channel list */
364                         selchannel = (struct select_channel *)MALLOC(sizeof(struct select_channel));
365                         memuse++;
366                         /* set value */
367                         selchannel->channel = val;
368                         /* tail port */
369                         selchannelp = &ifport->out_channel;
370                         while(*selchannelp)
371                                 selchannelp = &((*selchannelp)->next);
372                         *selchannelp = selchannel;
373                 }
374         }
375         return(0);
376 }
377 static int inter_channel_in(struct interface *interface, char *filename, int line, char *parameter, char *value)
378 {
379         struct interface_port *ifport;
380         struct select_channel *selchannel, **selchannelp;
381         int val;
382         char *p, *el;
383
384         /* port in chain ? */
385         if (!interface->ifport)
386         {
387                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
388                 return(-1);
389         }
390         /* goto end of chain */
391         ifport = interface->ifport;
392         while(ifport->next)
393                 ifport = ifport->next;
394         p = value;
395         while(*p)
396         {
397                 el = p;
398                 p = get_seperated(p);
399                 if (ifport->in_channel) if (ifport->in_channel->channel == CHANNEL_FREE)
400                 {
401                         SPRINT(interface_error, "Error in %s (line %d): parameter '%s' has values behind 'free' keyword. They has no effect.\n", filename, line, parameter);
402                                 return(-1);
403                 }
404                 if (!strcasecmp(el, "free"))
405                 {
406                         val = CHANNEL_FREE;
407                         goto selchannel;
408                 } else
409                 {
410                         val = get_number(el);
411                         if (val == -1)
412                         {
413                                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects a comma seperated list of channel numbers and 'free'.\n", filename, line, parameter);
414                                 return(-1);
415                         }
416
417                         if (val<1 || val==16 || val>126)
418                         {
419                                 SPRINT(interface_error, "Error in %s (line %d): channel '%d' out of range.\n", filename, line, val);
420                                 return(-1);
421                         }
422                         selchannel:
423                         /* add to select-channel list */
424                         selchannel = (struct select_channel *)MALLOC(sizeof(struct select_channel));
425                         memuse++;
426                         /* set value */
427                         selchannel->channel = val;
428                         /* tail port */
429                         selchannelp = &ifport->in_channel;
430                         while(*selchannelp)
431                                 selchannelp = &((*selchannelp)->next);
432                         *selchannelp = selchannel;
433                 }
434         }
435         return(0);
436 }
437 static int inter_timeouts(struct interface *interface, char *filename, int line, char *parameter, char *value)
438 {
439         struct interface_port *ifport;
440         struct select_channel *selchannel, **selchannelp;
441         int val;
442         char *p, *el;
443
444         /* port in chain ? */
445         if (!interface->ifport)
446         {
447                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
448                 return(-1);
449         }
450         /* goto end of chain */
451         ifport = interface->ifport;
452         while(ifport->next)
453                 ifport = ifport->next;
454         p = value;
455         if (!*p)
456         {
457                 nofive:
458                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects five timeout values.\n", filename, line, parameter);
459                 return(-1);
460         }
461         el = p;
462         p = get_seperated(p);
463         ifport->tout_setup = atoi(el);
464         if (!*p)
465                 goto nofive;
466         el = p;
467         p = get_seperated(p);
468         ifport->tout_dialing = atoi(el);
469         if (!*p)
470                 goto nofive;
471         el = p;
472         p = get_seperated(p);
473         ifport->tout_proceeding = atoi(el);
474         if (!*p)
475                 goto nofive;
476         el = p;
477         p = get_seperated(p);
478         ifport->tout_alerting = atoi(el);
479         if (!*p)
480                 goto nofive;
481         el = p;
482         p = get_seperated(p);
483         ifport->tout_disconnect = atoi(el);
484         return(0);
485 }
486 static int inter_msn(struct interface *interface, char *filename, int line, char *parameter, char *value)
487 {
488         struct interface_msn *ifmsn, **ifmsnp;
489         char *p, *el;
490
491         if (!value[0])
492         {
493                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects one MSN number or a list.\n", filename, line, parameter);
494                 return(-1);
495         }
496         if (interface->ifscreen_in)
497         {
498                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' not allowed with 'screen_in' parameter.\n", filename, line, parameter);
499                 return(-1);
500         }
501
502         /* process list */
503         p = value;
504         while(*p)
505         {
506                 el = p;
507                 p = get_seperated(p);
508                 /* add MSN to list */
509                 ifmsn = (struct interface_msn *)MALLOC(sizeof(struct interface_msn));
510                 memuse++;
511                 /* set value */
512                 SCPY(ifmsn->msn, el);
513                 /* tail port */
514                 ifmsnp = &interface->ifmsn;
515                 while(*ifmsnp)
516                         ifmsnp = &((*ifmsnp)->next);
517                 *ifmsnp = ifmsn;
518         }
519         return(0);
520 }
521 static int inter_screen(struct interface_screen **ifscreenp, struct interface *interface, char *filename, int line, char *parameter, char *value)
522 {
523         struct interface_screen *ifscreen;
524         char *p, *el;
525
526         if (!value[0])
527         {
528                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects old caller ID and new caller ID.\n", filename, line, parameter);
529                 return(-1);
530         }
531         /* add screen entry to list*/
532         ifscreen = (struct interface_screen *)MALLOC(sizeof(struct interface_screen));
533         memuse++;
534         ifscreen->match_type = -1; /* unchecked */
535         ifscreen->match_present = -1; /* unchecked */
536         ifscreen->result_type = -1; /* unchanged */
537         ifscreen->result_present = -1; /* unchanged */
538         /* tail port */
539         while(*ifscreenp)
540                 ifscreenp = &((*ifscreenp)->next);
541         *ifscreenp = ifscreen;
542 //      printf("interface=%s\n", interface->name);
543         /* get match */
544         p = value;
545         while(*p)
546         {
547                 el = p;
548                 p = get_seperated(p);
549                 if (!strcasecmp(el, "unknown"))
550                 {
551                         if (ifscreen->match_type != -1)
552                         {
553                                 typeerror:
554                                 SPRINT(interface_error, "Error in %s (line %d): number type already set earlier.\n", filename, line, parameter);
555                                 return(-1);
556                         }
557                         ifscreen->match_type = INFO_NTYPE_UNKNOWN;
558                 } else
559                 if (!strcasecmp(el, "subscriber"))
560                 {
561                         if (ifscreen->match_type != -1)
562                                 goto typeerror;
563                         ifscreen->match_type = INFO_NTYPE_SUBSCRIBER;
564                 } else
565                 if (!strcasecmp(el, "national"))
566                 {
567                         if (ifscreen->match_type != -1)
568                                 goto typeerror;
569                         ifscreen->match_type = INFO_NTYPE_NATIONAL;
570                 } else
571                 if (!strcasecmp(el, "international"))
572                 {
573                         if (ifscreen->match_type != -1)
574                                 goto typeerror;
575                         ifscreen->match_type = INFO_NTYPE_INTERNATIONAL;
576                 } else
577                 if (!strcasecmp(el, "allowed"))
578                 {
579                         if (ifscreen->match_present != -1)
580                         {
581                                 presenterror:
582                                 SPRINT(interface_error, "Error in %s (line %d): presentation type already set earlier.\n", filename, line);
583                                 return(-1);
584                         }
585                         ifscreen->match_present = INFO_PRESENT_ALLOWED;
586                 } else
587                 if (!strcasecmp(el, "restrict") || !strcasecmp(el, "restricted"))
588                 {
589                         if (ifscreen->match_present != -1)
590                                 goto presenterror;
591                         ifscreen->match_present = INFO_PRESENT_RESTRICTED;
592                 } else {
593                         SCPY(ifscreen->match, el);
594                         /* check for % at the end */
595                         if (strchr(el, '%'))
596                         {
597                                 if (strchr(el, '%') != el+strlen(el)-1)
598                                 {
599                                         SPRINT(interface_error, "Error in %s (line %d): %% joker found, but must at the end.\n", filename, line, parameter);
600                                         return(-1);
601                                 }
602                         }
603                         break;
604                 }
605         }
606         if (ifscreen->match[0] == '\0')
607         {
608                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects old caller ID.\n", filename, line, parameter);
609                 return(-1);
610         }
611         /* get result */
612         while(*p)
613         {
614                 el = p;
615                 p = get_seperated(p);
616                 if (!strcasecmp(el, "unknown"))
617                 {
618                         if (ifscreen->result_type != -1)
619                                 goto typeerror;
620                         ifscreen->result_type = INFO_NTYPE_UNKNOWN;
621                 } else
622                 if (!strcasecmp(el, "subscriber"))
623                 {
624                         if (ifscreen->result_type != -1)
625                                 goto typeerror;
626                         ifscreen->result_type = INFO_NTYPE_SUBSCRIBER;
627                 } else
628                 if (!strcasecmp(el, "national"))
629                 {
630                         if (ifscreen->result_type != -1)
631                                 goto typeerror;
632                         ifscreen->result_type = INFO_NTYPE_NATIONAL;
633                 } else
634                 if (!strcasecmp(el, "international"))
635                 {
636                         if (ifscreen->result_type != -1)
637                                 goto typeerror;
638                         ifscreen->result_type = INFO_NTYPE_INTERNATIONAL;
639                 } else
640                 if (!strcasecmp(el, "allowed"))
641                 {
642                         if (ifscreen->result_present != -1)
643                                 goto presenterror;
644                         ifscreen->result_present = INFO_PRESENT_ALLOWED;
645                 } else
646                 if (!strcasecmp(el, "restrict") || !strcasecmp(el, "restricted"))
647                 {
648                         if (ifscreen->result_present != -1)
649                                 goto presenterror;
650                         ifscreen->result_present = INFO_PRESENT_RESTRICTED;
651                 } else {
652                         SCPY(ifscreen->result, el);
653                         /* check for % at the end */
654                         if (strchr(el, '%'))
655                         {
656                                 if (strchr(el, '%') != el+strlen(el)-1)
657                                 {
658                                         SPRINT(interface_error, "Error in %s (line %d): %% joker found, but must at the end.\n", filename, line, parameter);
659                                         return(-1);
660                                 }
661                         }
662                         break;
663                 }
664         }
665         if (ifscreen->result[0] == '\0')
666         {
667                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects new caller ID.\n", filename, line, parameter);
668                 return(-1);
669         }
670         return(0);
671 }
672 static int inter_screen_in(struct interface *interface, char *filename, int line, char *parameter, char *value)
673 {
674         if (interface->ifmsn)
675         {
676                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' not allowed with 'msn' parameter.\n", filename, line, parameter);
677                 return(-1);
678         }
679
680         return(inter_screen(&interface->ifscreen_in, interface, filename, line, parameter, value));
681 }
682 static int inter_screen_out(struct interface *interface, char *filename, int line, char *parameter, char *value)
683 {
684         return(inter_screen(&interface->ifscreen_out, interface, filename, line, parameter, value));
685 }
686 static int inter_nodtmf(struct interface *interface, char *filename, int line, char *parameter, char *value)
687 {
688         struct interface_port *ifport;
689
690         /* port in chain ? */
691         if (!interface->ifport)
692         {
693                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
694                 return(-1);
695         }
696         /* goto end of chain */
697         ifport = interface->ifport;
698         while(ifport->next)
699                 ifport = ifport->next;
700         ifport->nodtmf = 1;
701         return(0);
702 }
703 static int inter_filter(struct interface *interface, char *filename, int line, char *parameter, char *value)
704 {
705         char *p, *q;
706
707         /* seperate parameter from filter */
708         p = value;
709         while(*p > 32)
710                 p++;
711         if (*p)
712         {
713                 *p++ = 0;
714                 while(*p > 0 && *p <= 32)
715                         p++;
716         }
717
718         if (!strcasecmp(value, "gain"))
719         {
720                 q = p;
721                 while(*q > 32)
722                         q++;
723                 if (*q)
724                 {
725                         *q++ = 0;
726                         while(*q > 0 && *q <= 32)
727                                 q++;
728                 }
729                 if (*p == 0 || *q == 0)
730                 {
731                         SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' expects two gain values.\n", filename, line, parameter, value);
732                         return(-1);
733                 }
734                 if (atoi(p)<-8 || atoi(p)>8 || atoi(q)<-8 || atoi(q)>8)
735                 {
736                         SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' gain values not in range. (-8...8)\n", filename, line, parameter, value);
737                         return(-1);
738                 }
739                 interface->tx_gain = atoi(p);
740                 interface->rx_gain = atoi(q);
741         } else
742         if (!strcasecmp(value, "pipeline"))
743         {
744                 if (*p == 0)
745                 {
746                         SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' expects pipeline string.\n", filename, line, parameter, value);
747                         return(-1);
748                 }
749                 SCPY(interface->pipeline, p);
750         } else
751         if (!strcasecmp(value, "blowfish"))
752         {
753                 unsigned char key[56];
754                 int l;
755                 
756                 if (!!strncmp(p, "0x", 2))
757                 {
758                         SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' expects blowfish key starting with '0x'.\n", filename, line, parameter, value);
759                         return(-1);
760                 }
761                 p += 2;
762                 l = 0; 
763                 while(*p)
764                 {
765                         if (l == 56)
766                         {
767                                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' key too long.\n", filename, line, parameter, value);
768                                 return(-1);
769                         }
770                         if (*p >= '0' && *p <= '9')
771                                 key[l] = (*p-'0')<<4;
772                         else if (*p >= 'a' && *p <= 'f')
773                                 key[l] = (*p-'a'+10)<<4;
774                         else if (*p >= 'A' && *p <= 'F')
775                                 key[l] = (*p-'A'+10)<<4;
776                         else
777                         {
778                                 digout:
779                                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' key has digits out of range. (0...9, a...f)\n", filename, line, parameter, value);
780                                 return(-1);
781                         }
782                         p++;
783                         if (*p == 0)
784                         {
785                                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' key must end on an 8 bit boundary (two character boundary).\n", filename, line, parameter, value);
786                                 return(-1);
787                         }
788                         if (*p >= '0' && *p <= '9')
789                                 key[l] = (*p-'0')<<4;
790                         else if (*p >= 'a' && *p <= 'f')
791                                 key[l] = (*p-'a'+10)<<4;
792                         else if (*p >= 'A' && *p <= 'F')
793                                 key[l] = (*p-'A'+10)<<4;
794                         else
795                                 goto digout;
796                         p++;
797                         l++;
798                 }
799                 if (l < 4)
800                 {
801                         SPRINT(interface_error, "Error in %s (line %d): parameter '%s %s' key must be at least 4 bytes (8 characters).\n", filename, line, parameter, value);
802                         return(-1);
803                 }
804                 memcpy(interface->bf_key, key, l);
805                 interface->bf_len = l;
806         } else
807         {
808                 SPRINT(interface_error, "Error in %s (line %d): parameter '%s' has unknown filter '%s'.\n", filename, line, parameter, value);
809                 return(-1);
810         }
811         return(0);
812 }
813
814
815 /*
816  * structure of parameters
817  */
818 struct interface_param interface_param[] = {
819         { "extension", &inter_extension, "",
820         "If keyword is given, calls to interface are handled as internal extensions."},
821         {"tones", &inter_tones, "yes | no",
822         "Interface generates tones during call setup and release, or not.\nBy default only NT-mode ports generate tones."},
823
824         {"earlyb", &inter_earlyb, "yes | no",
825         "Interface receives and bridges tones during call setup and release, or not.\nBy default only TE-mode ports receive tones."},
826
827         {"hunt", &inter_hunt, "linear | roundrobin",
828         "Select the algorithm for selecting port with free channel."},
829
830         {"port", &inter_port, "<number>",
831         "Give exactly one port for this interface.\nTo give multiple ports, add more lines with port parameters."},
832
833         {"block", &inter_block, "",
834         "If keyword is given, calls on this interface are blocked.\n"
835         "This parameter must follow a 'port' parameter."},
836
837         {"ptp", &inter_ptp, "",
838         "The given port above is opened as point-to-point.\n"
839         "This is required on NT-mode ports that are multipoint by default.\n"
840         "This parameter must follow a 'port' parameter."},
841
842         {"ptmp", &inter_ptmp, "",
843         "The given port above is opened as point-to-multipoint.\n"
844         "This is required on PRI NT-mode ports that are point-to-point by default.\n"
845         "This parameter must follow a 'port' parameter."},
846         {"nt", &inter_nt, "",
847         "The given port above is opened in NT-mode.\n"
848 #ifdef SOCKET_MISDN
849         "This is required on interfaces that support both NT-mode and TE-mode.\n"
850 #else
851         "This parameter is only required for socket based mISDN driver.\n"
852 #endif
853         "This parameter must follow a 'port' parameter."},
854
855         {"channel-out", &inter_channel_out, "[force,][<number>][,...][,free][,any][,no]",
856         "Channel selection list for all outgoing calls to the interface.\n"
857         "A free channels is searched in order of appearance.\n"
858         "This parameter must follow a 'port' parameter.\n"
859         " force - Forces the selected port with no acceptable alternative (see DSS1).\n"
860         " <number>[,...] - List of channels to search.\n"
861         " free - Select any free channel\n"
862         " any - On outgoing calls, signal 'any channel acceptable'. (see DSS1)\n"
863         " no - Signal 'no channel available' aka 'call waiting'. (see DSS1)"},
864
865         {"channel-in", &inter_channel_in, "[<number>][,...][,free]",
866         "Channel selection list for all incoming calls from the interface.\n"
867         "A free channels is accepted if in the list.\n"
868         "If any channel was requested, the first free channel found is selected.\n"
869         "This parameter must follow a 'port' parameter.\n"
870         " <number>[,...] - List of channels to accept.\n"
871         " free - Accept any free channel"},
872
873         {"timeouts", &inter_timeouts, "<setup> <dialing> <proceeding> <alerting> <disconnect>",
874         "Timeout values for call states. They are both for incoming and outgoing states.\n"
875         "The default is 120 seconds for all states. Use 0 to disable.\n"
876         "This parameter must follow a 'port' parameter.\n"},
877
878         {"msn", &inter_msn, "<default MSN>,[<additional MSN>[,...]]",
879         "Incoming caller ID is checked against given MSN numbers.\n"
880         "If the caller ID is not found in this list, it is overwritten by the first MSN"},
881
882         {"screen-in", &inter_screen_in, "[options] <old caller ID>[%] [options] <new caller ID>[%]",
883         "Adds an entry for incoming calls to the caller ID screen list.\n"
884         "If the given 'old caller ID' matches, it is replaced by the 'new caller ID'\n"
885         "If '%' is given after old caller ID, it matches even if caller ID has\n"
886         "additional digits.\n"
887         "If '%' is given after mew caller ID, additinal digits of the 'old caller ID'\n"
888         "are added.\n"
889         "Options can be:\n"
890         " unknown | subsciber | national | international - Change caller ID type.\n"
891         " present | restrict - Change presentation of caller ID."},
892                 
893         {"screen-out", &inter_screen_out, "[options] <old caller ID>[%] [options] <new caller ID>[%]",
894         "Adds an entry for outgoing calls to the caller ID screen list.\n"
895         "See 'screen-in' for help."},
896
897         {"nodtmf", &inter_nodtmf, "",
898         "Disables DTMF detection for this interface.\n"
899         "This parameter must follow a 'port' parameter."},
900
901 #if 0
902         {"layer2keep", &inter_layer2keep, "yes | no",
903         "By default, layer 2 is establised and kept up on PTP interfaces.\n"
904         ".\n"
905         "This parameter must follow a 'port' parameter."},
906 #endif
907
908         {"filter", &inter_filter, "<filter> <parameters>",
909         "Adds/appends a filter. Filters are ordered in transmit direction.\n"
910         "gain <tx-volume> <rx-volume> - Changes volume (-8 .. 8)\n"
911         "pipeline <string> - Sets echo cancelation pipeline.\n"
912         "blowfish <key> - Adds encryption. Key must be 4-56 bytes (8-112 hex characters."},
913
914         {NULL, NULL, NULL, NULL}
915 };
916
917 /* read interfaces
918  *
919  * read settings from interface.conf
920  */
921 char interface_error[256];
922 struct interface *read_interfaces(void)
923 {
924         FILE                    *fp = NULL;
925         char                    filename[128];
926         char                    *p;
927         unsigned int            line, i;
928         char                    buffer[256];
929         struct interface        *interface = NULL, /* in case no interface */
930                                 **interfacep = &interface_newlist;
931         char                    parameter[128];
932         char                    value[256];
933         int                     expecting = 1; /* expecting new interface */
934         struct interface_param  *ifparam;
935
936         if (interface_newlist != NULL)
937                 FATAL("list is not empty.\n");
938         interface_error[0] = '\0';
939         SPRINT(filename, "%s/interface.conf", INSTALL_DATA);
940
941         if (!(fp = fopen(filename,"r")))
942         {
943                 SPRINT(interface_error, "Cannot open '%s'\n", filename);
944                 goto error;
945         }
946
947         line=0;
948         while((fgets(buffer,sizeof(buffer),fp)))
949         {
950                 buffer[sizeof(buffer)-1]=0;
951                 if (buffer[0]) buffer[strlen(buffer)-1]=0;
952                 p=buffer;
953                 line++;
954
955                 while(*p <= 32) /* skip spaces */
956                 {
957                         if (*p == 0)
958                                 break;
959                         p++;
960                 }
961                 if (*p==0 || *p=='#') /* ignore comments and empty line */
962                         continue;
963
964                 parameter[0]=0;
965                 value[0]=0;
966                 i=0; /* read parameter */
967                 while(*p > 32)
968                 {
969                         if (i+1 >= sizeof(parameter))
970                         {
971                                 SPRINT(interface_error, "Error in %s (line %d): parameter name too long.\n",filename,line);
972                                 goto error;
973                         }
974                         parameter[i+1] = '\0';
975                         parameter[i++] = *p++;
976                 }
977
978                 while(*p <= 32) /* skip spaces */
979                 {
980                         if (*p == 0)
981                                 break;
982                         p++;
983                 }
984
985                 if (*p!=0 && *p!='#') /* missing name */
986                 {
987                         i=0; /* read until end */
988                         while(*p!=0 && *p!='#')
989                         {
990                                 if (i+1 >= sizeof(value))
991                                 {
992                                         SPRINT(interface_error, "Error in %s (line %d): value too long.\n", filename, line);
993                                         goto error;
994                                 }
995                                 value[i+1] = '\0';
996                                 value[i++] = *p++;
997                         }
998
999                         /* remove trailing spaces from value */
1000                         while(i)
1001                         {
1002                                 if (value[i-1]==0 || value[i-1]>32)
1003                                         break;
1004                                 value[i-1] = '\0';
1005                                 i--;
1006                         }
1007                 }
1008
1009                 /* check for interface name as first statement */
1010                 if (expecting && parameter[0]!='[')
1011                 {
1012                         SPRINT(interface_error, "Error in %s (line %d): expecting interface name inside [ and ], but got: '%s'.\n", filename, line, parameter);
1013                         goto error;
1014                 }
1015                 expecting = 0;
1016
1017                 /* check for new interface */
1018                 if (parameter[0] == '[')
1019                 {
1020                         if (parameter[strlen(parameter)-1] != ']')
1021                         {
1022                                 SPRINT(interface_error, "Error in %s (line %d): expecting interface name inside [ and ], but got: '%s'.\n", filename, line, parameter);
1023                                 goto error;
1024                         }
1025                         parameter[strlen(parameter)-1] = '\0';
1026
1027                         /* check if interface name already exists */
1028                         interface = interface_newlist;
1029                         while(interface)
1030                         {
1031                                 if (!strcasecmp(interface->name, parameter+1))
1032                                 {
1033                                         SPRINT(interface_error, "Error in %s (line %d): interface name '%s' already defined above.\n", filename, line, parameter+1);
1034                                         goto error;
1035                                 }
1036                                 interface = interface->next;
1037                         }
1038
1039                         /* append interface to new list */
1040                         interface = (struct interface *)MALLOC(sizeof(struct interface));
1041                         memuse++;
1042
1043                         /* name interface */
1044                         SCPY(interface->name, parameter+1);
1045
1046                         /* attach */
1047                         *interfacep = interface;
1048                         interfacep = &interface->next;
1049
1050                         continue;
1051                 }
1052
1053                 ifparam = interface_param;
1054                 while(ifparam->name)
1055                 {
1056                         if (!strcasecmp(parameter, ifparam->name))
1057                         {
1058                                 if (ifparam->func(interface, filename, line, parameter, value))
1059                                         goto error;
1060                                 break;
1061                         }
1062                         ifparam++;
1063                 }
1064                 if (ifparam->name)
1065                         continue;
1066
1067                 SPRINT(interface_error, "Error in %s (line %d): unknown parameter: '%s'.\n", filename, line, parameter);
1068                 goto error;
1069         }
1070
1071         if (fp) fclose(fp);
1072         return(interface_newlist);
1073 error:
1074         PERROR_RUNTIME("%s", interface_error);
1075         if (fp) fclose(fp);
1076         free_interfaces(interface_newlist);
1077         interface_newlist = NULL;
1078         return(NULL);
1079 }
1080
1081
1082 /*
1083  * freeing chain of interfaces
1084  */
1085 void free_interfaces(struct interface *interface)
1086 {
1087         void *temp;
1088         struct interface_port *ifport;
1089         struct select_channel *selchannel;
1090         struct interface_msn *ifmsn;
1091         struct interface_screen *ifscreen;
1092
1093         while(interface)
1094         {
1095                 ifport = interface->ifport;
1096                 while(ifport)
1097                 {
1098                         selchannel = ifport->in_channel;
1099                         while(selchannel)
1100                         {
1101                                 temp = selchannel;
1102                                 selchannel = selchannel->next;
1103                                 FREE(temp, sizeof(struct select_channel));
1104                                 memuse--;
1105                         }
1106                         selchannel = ifport->out_channel;
1107                         while(selchannel)
1108                         {
1109                                 temp = selchannel;
1110                                 selchannel = selchannel->next;
1111                                 FREE(temp, sizeof(struct select_channel));
1112                                 memuse--;
1113                         }
1114                         temp = ifport;
1115                         ifport = ifport->next;
1116                         FREE(temp, sizeof(struct interface_port));
1117                         memuse--;
1118                 }
1119                 ifmsn = interface->ifmsn;
1120                 while(ifmsn)
1121                 {
1122                         temp = ifmsn;
1123                         ifmsn = ifmsn->next;
1124                         FREE(temp, sizeof(struct interface_msn));
1125                         memuse--;
1126                 }
1127                 ifscreen = interface->ifscreen_in;
1128                 while(ifscreen)
1129                 {
1130                         temp = ifscreen;
1131                         ifscreen = ifscreen->next;
1132                         FREE(temp, sizeof(struct interface_screen));
1133                         memuse--;
1134                 }
1135                 ifscreen = interface->ifscreen_out;
1136                 while(ifscreen)
1137                 {
1138                         temp = ifscreen;
1139                         ifscreen = ifscreen->next;
1140                         FREE(temp, sizeof(struct interface_screen));
1141                         memuse--;
1142                 }
1143                 temp = interface;
1144                 interface = interface->next;
1145                 FREE(temp, sizeof(struct interface));
1146                 memuse--;
1147         }
1148 }
1149
1150 /*
1151  * defaults of ports if not specified by config
1152  */
1153 static void set_defaults(struct interface_port *ifport)
1154 {
1155         /* default channel selection list */
1156         if (!ifport->out_channel)
1157                 default_out_channel(ifport);
1158         if (!ifport->in_channel)
1159                 default_in_channel(ifport);
1160         /* default is_tones */
1161         if (ifport->interface->is_tones)
1162                 ifport->mISDNport->tones = (ifport->interface->is_tones==IS_YES);
1163         else
1164                 ifport->mISDNport->tones = (ifport->mISDNport->ntmode)?1:0;
1165         /* default is_earlyb */
1166         if (ifport->interface->is_earlyb)
1167                 ifport->mISDNport->earlyb = (ifport->interface->is_earlyb==IS_YES);
1168         else
1169                 ifport->mISDNport->earlyb = (ifport->mISDNport->ntmode)?0:1;
1170         /* set locally flag */
1171         if (ifport->interface->extension)
1172                 ifport->mISDNport->locally = 1;
1173         else
1174                 ifport->mISDNport->locally = 0;
1175 }
1176
1177
1178 /*
1179  * all links between mISDNport and interface are made
1180  * unused mISDNports are closed, new mISDNports are opened
1181  * also set default select_channel lists
1182  */
1183 void relink_interfaces(void)
1184 {
1185         struct mISDNport *mISDNport;
1186         struct interface *interface;
1187         struct interface_port *ifport;
1188
1189         /* unlink all mISDNports */
1190         mISDNport = mISDNport_first;
1191         while(mISDNport)
1192         {
1193                 mISDNport->ifport = NULL;
1194                 mISDNport = mISDNport->next;
1195         }
1196
1197         /* relink existing mISDNports */
1198         interface = interface_newlist;
1199         while(interface)
1200         {
1201                 ifport = interface->ifport;
1202                 while(ifport)
1203                 {
1204                         mISDNport = mISDNport_first;
1205                         while(mISDNport)
1206                         {
1207                                 if (mISDNport->portnum == ifport->portnum)
1208                                 {
1209                                         ifport->mISDNport = mISDNport;
1210                                         mISDNport->ifport = ifport;
1211                                         set_defaults(ifport);
1212                                 }
1213                                 mISDNport = mISDNport->next;
1214                         }
1215                         ifport = ifport->next;
1216                 }
1217                 interface = interface->next;
1218         }
1219
1220         /* close unused mISDNports */
1221         closeagain:
1222         mISDNport = mISDNport_first;
1223         while(mISDNport)
1224         {
1225                 if (mISDNport->ifport == NULL)
1226                 {
1227                         PDEBUG(DEBUG_ISDN, "Port %d is not used anymore and will be closed\n", mISDNport->portnum);
1228                         /* remove all port objects and destroy port */
1229                         mISDNport_close(mISDNport);
1230                         goto closeagain;
1231                 }
1232                 mISDNport = mISDNport->next;
1233         }
1234
1235         /* open and link new mISDNports */
1236         interface = interface_newlist;
1237         while(interface)
1238         {
1239                 ifport = interface->ifport;
1240                 while(ifport)
1241                 {
1242                         if (!ifport->mISDNport)
1243                         {
1244                                 load_port(ifport);
1245                         }
1246                         ifport = ifport->next;
1247                 }
1248                 interface = interface->next;
1249         }
1250
1251 }
1252
1253
1254 /*
1255  * load port
1256  */
1257 void load_port(struct interface_port *ifport)
1258 {
1259         struct mISDNport *mISDNport;
1260
1261         /* open new port */
1262         mISDNport = mISDNport_open(ifport->portnum, ifport->ptp, ifport->ptmp, ifport->nt, ifport->interface);
1263         if (mISDNport)
1264         {
1265                 /* link port */
1266                 ifport->mISDNport = mISDNport;
1267                 mISDNport->ifport = ifport;
1268                 set_defaults(ifport);
1269         } else
1270         {
1271                 ifport->block = 2; /* not available */
1272         }
1273 }
1274
1275 /*
1276  * give summary of interface syntax
1277  */
1278 void doc_interface(void)
1279 {
1280         struct interface_param *ifparam;
1281         
1282         printf("Syntax overview\n");
1283         printf("---------------\n\n");
1284
1285         printf("[<name>]\n");
1286         ifparam = interface_param;
1287         while(ifparam->name)
1288         {
1289                 printf("%s %s\n", ifparam->name, ifparam->usage);
1290                 ifparam++;
1291         }
1292
1293         ifparam = interface_param;
1294         while(ifparam->name)
1295         {
1296                 printf("\nParameter: %s %s\n", ifparam->name, ifparam->usage);
1297                 printf("%s\n", ifparam->help);
1298                 ifparam++;
1299         }
1300 }
1301
1302
1303 /* screen caller id
1304  * out==0: incoming caller id, out==1: outgoing caller id
1305  */
1306 void do_screen(int out, char *id, int idsize, int *type, int *present, struct interface *interface)
1307 {
1308         char                    *msn1;
1309         struct interface_msn    *ifmsn;
1310         struct interface_screen *ifscreen;
1311         char suffix[64];
1312
1313         /* screen incoming caller id */
1314         if (!out)
1315         {
1316                 /* check for MSN numbers, use first MSN if no match */
1317                 msn1 = NULL;
1318                 ifmsn = interface->ifmsn;
1319                 while(ifmsn)
1320                 {
1321                         if (!msn1)
1322                                 msn1 = ifmsn->msn;
1323                         if (!strcmp(ifmsn->msn, id))
1324                         {
1325                                 break;
1326                         }
1327                         ifmsn = ifmsn->next;
1328                 }
1329                 if (ifmsn)
1330                 {
1331                         start_trace(0, interface, numberrize_callerinfo(id, *type), NULL, DIRECTION_IN, 0, 0, "SCREEN (fount in MSN list)");
1332                         add_trace("msn", NULL, "%s", id);
1333                         end_trace();
1334                 }
1335                 if (!ifmsn && msn1) // not in list, first msn given
1336                 {
1337                         start_trace(0, interface, numberrize_callerinfo(id, *type), NULL, DIRECTION_IN, 0, 0, "SCREEN (not fount in MSN list)");
1338                         add_trace("msn", "given", "%s", id);
1339                         add_trace("msn", "used", "%s", msn1);
1340                         end_trace();
1341                         UNCPY(id, msn1, idsize);
1342                         id[idsize-1] = '\0';
1343                 }
1344         }
1345
1346         /* check screen list */
1347         if (out)
1348                 ifscreen = interface->ifscreen_out;
1349         else
1350                 ifscreen = interface->ifscreen_in;
1351         while (ifscreen)
1352         {
1353                 if (ifscreen->match_type==-1 || ifscreen->match_type==*type)
1354                 if (ifscreen->match_present==-1 || ifscreen->match_present==*present)
1355                 {
1356                         if (strchr(ifscreen->match,'%'))
1357                         {
1358                                 if (!strncmp(ifscreen->match, id, strchr(ifscreen->match,'%')-ifscreen->match))
1359                                         break;
1360                         } else
1361                         {
1362                                 if (!strcmp(ifscreen->match, id))
1363                                         break;
1364                         }
1365                 }
1366                 ifscreen = ifscreen->next;
1367         }
1368         if (ifscreen) // match
1369         {
1370                 start_trace(0, interface, numberrize_callerinfo(id, *type), NULL, out?DIRECTION_OUT:DIRECTION_IN, 0, 0, "SCREEN (fount in screen list)");
1371                 switch(*type)
1372                 {
1373                         case INFO_NTYPE_UNKNOWN:
1374                         add_trace("given", "type", "unknown");
1375                         break;
1376                         case INFO_NTYPE_SUBSCRIBER:
1377                         add_trace("given", "type", "subscriber");
1378                         break;
1379                         case INFO_NTYPE_NATIONAL:
1380                         add_trace("given", "type", "national");
1381                         break;
1382                         case INFO_NTYPE_INTERNATIONAL:
1383                         add_trace("given", "type", "international");
1384                         break;
1385                 }
1386                 switch(*present)
1387                 {
1388                         case INFO_PRESENT_ALLOWED:
1389                         add_trace("given", "present", "allowed");
1390                         break;
1391                         case INFO_PRESENT_RESTRICTED:
1392                         add_trace("given", "present", "restricted");
1393                         break;
1394                         case INFO_PRESENT_NOTAVAIL:
1395                         add_trace("given", "present", "not available");
1396                         break;
1397                 }
1398                 add_trace("given", "id", "%s", id[0]?id:"<empty>");
1399                 if (ifscreen->result_type != -1)
1400                 {
1401                         *type = ifscreen->result_type;
1402                         switch(*type)
1403                         {
1404                                 case INFO_NTYPE_UNKNOWN:
1405                                 add_trace("used", "type", "unknown");
1406                                 break;
1407                                 case INFO_NTYPE_SUBSCRIBER:
1408                                 add_trace("used", "type", "subscriber");
1409                                 break;
1410                                 case INFO_NTYPE_NATIONAL:
1411                                 add_trace("used", "type", "national");
1412                                 break;
1413                                 case INFO_NTYPE_INTERNATIONAL:
1414                                 add_trace("used", "type", "international");
1415                                 break;
1416                         }
1417                 }
1418                 if (ifscreen->result_present != -1)
1419                 {
1420                         *present = ifscreen->result_present;
1421                         switch(*present)
1422                         {
1423                                 case INFO_PRESENT_ALLOWED:
1424                                 add_trace("used", "present", "allowed");
1425                                 break;
1426                                 case INFO_PRESENT_RESTRICTED:
1427                                 add_trace("used", "present", "restricted");
1428                                 break;
1429                                 case INFO_PRESENT_NOTAVAIL:
1430                                 add_trace("used", "present", "not available");
1431                                 break;
1432                         }
1433                 }
1434                 if (strchr(ifscreen->match,'%'))
1435                 {
1436                         SCPY(suffix, strchr(ifscreen->match,'%') - ifscreen->match + id);
1437                         UNCPY(id, ifscreen->result, idsize);
1438                         id[idsize-1] = '\0';
1439                         if (strchr(id,'%'))
1440                         {
1441                                 *strchr(id,'%') = '\0';
1442                                 UNCAT(id, suffix, idsize);
1443                                 id[idsize-1] = '\0';
1444                         }
1445                 } else
1446                 {
1447                         UNCPY(id, ifscreen->result, idsize);
1448                         id[idsize-1] = '\0';
1449                 }
1450                 add_trace("used", "id", "%s", id[0]?id:"<empty>");
1451                 end_trace();
1452         }
1453 }
1454