]> git.mxchange.org Git - flightgear.git/blob - utils/iaxclient/lib/libiax2/src/miniphone.c
Fix Windows warning during Windows compilation
[flightgear.git] / utils / iaxclient / lib / libiax2 / src / miniphone.c
1 /*
2  * Miniphone: A simple, command line telephone
3  *
4  * IAX Support for talking to Asterisk and other Gnophone clients
5  *
6  * Copyright (C) 1999, Linux Support Services, Inc.
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <fcntl.h>
19 #include <stdio.h>
20 #include <errno.h>
21 #include <sys/ioctl.h>
22 #include <iax-client.h>
23 #include <linux/soundcard.h>
24 #include <errno.h>
25 #include <sys/time.h>
26 #include <sys/signal.h>
27 #include <sys/time.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <gsm.h>
32 #include <miniphone.h>
33 #include <time.h>
34
35 #include "busy.h"
36 #include "dialtone.h"
37 #include "answer.h"
38 #include "ringtone.h"
39 #include "ring10.h"
40 #include "options.h"
41
42 #define FRAME_SIZE 160
43
44 static char callerid[80];
45
46 struct peer {
47         int time;
48         gsm gsmin;
49         gsm gsmout;
50
51         struct iax_session *session;
52         struct peer *next;
53 };
54
55 static char *audiodev = "/dev/dsp";
56 static int audiofd = -1;
57 static struct peer *peers;
58 static int answered_call = 0;
59
60 static struct peer *find_peer(struct iax_session *);
61 static int audio_setup(char *);
62 static void sighandler(int);
63 static void parse_args(FILE *, unsigned char *);
64 void do_iax_event(FILE *);
65 void call(FILE *, char *);
66 void answer_call(void);
67 void reject_call(void);
68 static void handle_event(FILE *, struct iax_event *e, struct peer *p);
69 void parse_cmd(FILE *, int, char **);
70 void issue_prompt(FILE *);
71 void dump_array(FILE *, char **);
72
73 struct sound {
74         short *data;
75         int datalen;
76         int samplen;
77         int silencelen;
78         int repeat;
79 };
80
81 static int cursound = -1;
82
83 static int sampsent = 0;
84 static int offset = 0;
85 static int silencelen = 0;
86 static int nosound = 0;
87
88 static int offhook = 0;
89 static int ringing = 0;
90
91 static int writeonly = 0;
92
93 static struct iax_session *registry = NULL;
94 static struct timeval regtime;
95
96 #define TONE_NONE     -1
97 #define TONE_RINGTONE 0
98 #define TONE_BUSY     1
99 #define TONE_CONGEST  2
100 #define TONE_RINGER   3
101 #define TONE_ANSWER   4
102 #define TONE_DIALTONE  5
103
104 #define OUTPUT_NONE    0
105 #define OUTPUT_SPEAKER 1
106 #define OUTPUT_HANDSET 2
107 #define OUTPUT_BOTH    3
108
109 static struct sound sounds[] = {
110         { ringtone, sizeof(ringtone)/2, 16000, 32000, 1 },
111         { busy, sizeof(busy)/2, 4000, 4000, 1 },
112         { busy, sizeof(busy)/2, 2000, 2000, 1 },
113         { ring10, sizeof(ring10)/2, 16000, 32000, 1 },
114         { answer, sizeof(answer)/2, 2200, 0, 0 },
115         { dialtone, sizeof(dialtone)/2, 8000, 0, 1 },
116 };
117
118 static char *help[] = {
119 "Welcome to the miniphone telephony client, the commands are as follows:\n",
120 "Help\t\t-\tDisplays this screen.",
121 "Help <Command>\t-\tInqueries specific information on a command.",
122 "Dial <Number>\t-\tDials the number supplied in the first arguement",
123 "Status\t\t-\tLists the current sessions and their current status.",
124 "Quit\t\t-\tShuts down the client.",
125 "",
126 0
127 };
128
129 static short silence[FRAME_SIZE];
130
131 static struct peer *most_recent_answer;
132 static struct iax_session *newcall = 0;
133
134 static struct peer *find_peer(struct iax_session *session)
135 {
136         struct peer *cur = peers;
137         while(cur) {
138                 if (cur->session == session)
139                         return cur;
140                 cur = cur->next;
141         }
142         return NULL;
143 }
144
145 static int audio_setup(char *dev)
146 {
147         int fd; 
148         int fmt = AFMT_S16_LE;
149         int channels = 1;
150         int speed = 8000;
151         int fragsize = (40 << 16) | 6;
152         if ( (fd = open(dev, O_RDWR | O_NONBLOCK)) < 0) {
153                 fprintf(stderr, "Unable to open audio device %s: %s\n", dev, strerror(errno));
154                 return -1;
155         }
156         if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) || (fmt != AFMT_S16_LE)) {
157                 fprintf(stderr, "Unable to set in signed linear format.\n");
158                 return -1;
159         }
160         if (ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0)) {
161                 fprintf(stderr, "Unable to set full duplex operation.\n");
162                 writeonly = 1;
163                 /* return -1; */
164         }
165         if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) || (channels != 1)) {
166                 fprintf(stderr, "Unable to set to mono\n");
167                 return -1;
168         }
169         if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) || (speed != 8000)) {
170                 fprintf(stderr, "Unable to set speed to 8000 hz\n");
171                 return -1;
172         }
173         if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fragsize)) {
174                 fprintf(stderr, "Unable to set fragment size...\n");
175                 return -1;
176         }
177
178         return fd;
179 }
180
181 static int send_sound(int soundfd)
182 {
183         /* Send FRAME_SIZE samples of whatever */
184         short myframe[FRAME_SIZE];
185         short *frame = NULL;
186         int total = FRAME_SIZE;
187         int amt=0;
188         int res;
189         int myoff;
190         audio_buf_info abi;
191         if (cursound > -1) {
192                 res = ioctl(soundfd, SNDCTL_DSP_GETOSPACE ,&abi);
193                 if (res) {
194                         fprintf(stderr,"Unable to read output space\n");
195                         return -1;
196                 }
197                 /* Calculate how many samples we can send, max */
198                 if (total > (abi.fragments * abi.fragsize / 2)) 
199                         total = abi.fragments * abi.fragsize / 2;
200                 res = total;
201                 if (sampsent < sounds[cursound].samplen) {
202                         myoff=0;
203                         while(total) {
204                                 amt = total;
205                                 if (amt > (sounds[cursound].datalen - offset)) 
206                                         amt = sounds[cursound].datalen - offset;
207                                 memcpy(myframe + myoff, sounds[cursound].data + offset, amt * 2);
208                                 total -= amt;
209                                 offset += amt;
210                                 sampsent += amt;
211                                 myoff += amt;
212                                 if (offset >= sounds[cursound].datalen)
213                                         offset = 0;
214                         }
215                         /* Set it up for silence */
216                         if (sampsent >= sounds[cursound].samplen) 
217                                 silencelen = sounds[cursound].silencelen;
218                         frame = myframe;
219                 } else {
220                         if (silencelen > 0) {
221                                 frame = silence;
222                                 silencelen -= res;
223                         } else {
224                                 if (sounds[cursound].repeat) {
225                                         /* Start over */
226                                         sampsent = 0;
227                                         offset = 0;
228                                 } else {
229                                         cursound = -1;
230                                         nosound = 0;
231                                 }
232                         }
233                 }
234 #if 0
235                 if (frame)
236                         printf("res is %d, frame[0] is %d\n", res, frame[0]);
237 #endif          
238                 res = write(soundfd, frame, res * 2);
239                 if (res > 0)
240                         return 0;
241                 return res;
242         }
243         return 0;
244 }
245
246 static int iax_regtimeout(int timeout)
247 {
248         if (timeout) {
249                 gettimeofday(&regtime, NULL);
250                 regtime.tv_sec += timeout;
251         } else {
252                 regtime.tv_usec = 0;
253                 regtime.tv_sec = 0;
254         }
255         return 0;
256 }
257
258 static int check_iax_register(void)
259 {
260         int res;
261         if (strlen(regpeer) && strlen(server)) {
262                 registry = iax_session_new();
263         
264                 res = iax_register(registry, server,regpeer,regsecret, refresh);
265         
266                 if (res) {
267                         fprintf(stderr, "Failed registration: %s\n", iax_errstr);
268                         return -1;
269                 }
270                 iax_regtimeout(5 * refresh / 6);
271         } else {
272                 iax_regtimeout(0);
273                 refresh = 60;
274         }
275         return 0;
276 }
277
278 static int check_iax_timeout(void)
279 {
280         struct timeval tv;
281         int ms;
282         if (!regtime.tv_usec || !regtime.tv_sec)
283                 return -1;
284         gettimeofday(&tv, NULL);
285         if ((tv.tv_usec >= regtime.tv_usec) && (tv.tv_sec >= regtime.tv_sec)) {
286                 check_iax_register();
287                 /* Have it check again soon */
288                 return 100;
289         }
290         ms = (regtime.tv_sec - tv.tv_sec) * 1000 + (regtime.tv_usec - tv.tv_usec) / 1000;
291         return ms;
292 }
293
294 static int gentone(int sound, int uninterruptible)
295 {
296         cursound = sound;
297         sampsent = 0;
298         offset = 0;
299         silencelen = 0;
300         nosound = uninterruptible;
301         printf("Sending tone %d\n", sound);
302         return 0;
303 }
304
305 void
306 sighandler(int sig)
307 {
308         if(sig == SIGHUP) {
309                 puts("rehashing!");
310         } else if(sig == SIGINT) {
311                 static int prev = 0;
312                 int cur;
313                 
314                 if ( (cur = time(0))-prev <= 5) {
315                         printf("Terminating!\n");
316                         exit(0);
317                 } else {
318                         prev = cur;
319                         printf("Press interrupt key again in the next %d seconds to really terminate\n", 5-(cur-prev));
320                 }
321         }
322 }
323
324 void
325 parse_args(FILE *f, unsigned char *cmd)
326 {
327         static char *argv[MAXARGS];
328         unsigned char *parse = cmd;
329         int argc = 0, t = 0;
330
331         // Don't mess with anything that doesn't exist...
332         if(!*parse)
333                 return;
334
335         bzero(argv, sizeof(argv));
336         while(*parse) {
337                 if(*parse < 33 || *parse > 128) {
338                         *parse = 0, t++;
339                         if(t > MAXARG) {
340                                 fprintf(f, "Warning: Argument exceeds maximum argument size, command ignored!\n");
341                                 return;
342                         }
343                 } else if(t || !argc) {
344                         if(argc == MAXARGS) {
345                                 fprintf(f, "Warning: Command ignored, too many arguments\n");
346                                 return;
347                         }
348                         argv[argc++] = parse;
349                         t = 0;
350                 }
351
352                 parse++;
353         }
354
355         if(argc)
356                 parse_cmd(f, argc, argv);
357 }
358
359 int
360 main(int argc, char *argv[])
361 {
362         int port;
363         int netfd;
364         int c, h=0, m, regm;
365         FILE *f;
366         int fd = STDIN_FILENO;
367         char rcmd[RBUFSIZE];
368         fd_set readfd;
369         fd_set writefd;
370         struct timeval timer;
371         struct timeval *timerptr = NULL;
372         gsm_frame fo;
373
374         load_options();
375
376         if (!strlen(callerid))
377                 gethostname(callerid, sizeof(callerid));
378
379         signal(SIGHUP, sighandler);
380         signal(SIGINT, sighandler);
381
382         if ( !(f = fdopen(fd, "w+"))) {
383                 fprintf(stderr, "Unable to create file on fd %d\n", fd);
384                 return -1;
385         }
386
387         if ( (audiofd = audio_setup(audiodev)) == -1) {
388                 fprintf(stderr, "Fatal error: failed to open sound device");
389                 return -1;
390         }
391
392         if ( (port = iax_init(0) < 0)) {
393                 fprintf(stderr, "Fatal error: failed to initialize iax with port %d\n", port);
394                 return -1;
395         }
396
397         iax_set_formats(AST_FORMAT_GSM);
398         netfd = iax_get_fd();
399
400         check_iax_register();
401
402         fprintf(f, "Text Based Telephony Client.\n\n");
403         issue_prompt(f);
404
405         timer.tv_sec = 0;
406         timer.tv_usec = 0;
407
408         while(1) {
409                 FD_ZERO(&readfd);
410                 FD_ZERO(&writefd);
411                 FD_SET(fd, &readfd);
412                         if(fd > h)
413                                 h = fd;
414                 if(answered_call && !writeonly) {
415                         FD_SET(audiofd, &readfd);
416                                 if(audiofd > h)
417                                         h = audiofd;
418                 }
419                 if (cursound > -1) {
420                         FD_SET(audiofd, &writefd);
421                         if (audiofd > h)
422                                 h = audiofd;
423                 }
424                 FD_SET(netfd, &readfd);
425                 if(netfd > h)
426                         h = netfd;
427
428                 if ( (c = select(h+1, &readfd, &writefd, 0, timerptr)) >= 0) {
429                         if(FD_ISSET(fd, &readfd)) {
430                                 if ( ( fgets(&*rcmd, 256, f))) {
431                                         rcmd[strlen(rcmd)-1] = 0;
432                                         parse_args(f, &*rcmd);
433                                 } else fprintf(f, "Fatal error: failed to read data!\n");
434
435                                 issue_prompt(f);
436                         }
437                         if(answered_call) {
438                                 if(FD_ISSET(audiofd, &readfd)) {
439                                 static int ret, rlen = 0;
440                                         static short rbuf[FRAME_SIZE];
441
442                                         if ( (ret = read(audiofd, rbuf + rlen, 2 * (FRAME_SIZE-rlen))) == -1) {
443                                                 puts("Failed to read audio.");
444                                                 return -1;
445                                         }
446                                         rlen += ret/2;
447                                         if(rlen == FRAME_SIZE) {
448                                                 rlen = 0;
449
450                                                 if(!most_recent_answer->gsmout)
451                                                         most_recent_answer->gsmout = gsm_create();
452
453                                                 gsm_encode(most_recent_answer->gsmout, rbuf, fo);
454                                                 if(iax_send_voice(most_recent_answer->session,
455                                                 AST_FORMAT_GSM, (char *)fo, sizeof(fo)) == -1)
456                                                         puts("Failed to send voice!");
457                                         }
458                                 }
459                         }
460                         do_iax_event(f);
461                         m = iax_time_to_next_event();
462                         if(m > -1) {
463                                 timerptr = &timer;
464                                 timer.tv_sec = m /1000;
465                                 timer.tv_usec = (m % 1000) * 1000;
466                         } else 
467                                 timerptr = 0;
468                         regm = check_iax_timeout();
469                         if (!timerptr || (m > regm)) {
470                                 timerptr = &timer;
471                                 timer.tv_sec = regm /1000;
472                                 timer.tv_usec = (regm % 1000) * 1000;
473                         }
474                         if (FD_ISSET(audiofd, &writefd)) {
475                                 send_sound(audiofd);
476                         }
477                 } else {
478                         if(errno == EINTR)
479                                 continue;
480                         fprintf(stderr, "Fatal error in select(): %s\n", strerror(errno));
481                         return -1;
482                 }
483         }
484         return 0;
485 }
486
487 void
488 do_iax_event(FILE *f) {
489         int sessions = 0;
490         struct iax_event *e = 0;
491         struct peer *peer;
492
493         while ( (e = iax_get_event(0))) {
494                 peer = find_peer(e->session);
495                 if(peer) {
496                         handle_event(f, e, peer);
497                 } else if (e->session == registry) {
498                         fprintf(stderr, "Registration complete: %s (%d)\n",
499                                 (e->event.regreply.status == IAX_REG_SUCCESS) ? "Success" : "Failed",
500                                 e->event.regreply.status);
501                         registry = NULL;
502                 } else {
503                         if(e->etype != IAX_EVENT_CONNECT) {
504                                 fprintf(stderr, "Huh? This is an event for a non-existant session?\n");
505                                 continue;
506                         }
507                         sessions++;
508
509                         if(sessions >= MAX_SESSIONS) {
510                                 fprintf(f, "Missed a call... too many sessions open.\n");
511                         }
512
513
514                         if(e->event.connect.callerid && e->event.connect.dnid)
515                                 fprintf(f, "Call from '%s' for '%s'", e->event.connect.callerid, 
516                                 e->event.connect.dnid);
517                         else if(e->event.connect.dnid) {
518                                 fprintf(f, "Call from '%s'", e->event.connect.dnid);
519                         } else if(e->event.connect.callerid) {
520                                 fprintf(f, "Call from '%s'", e->event.connect.callerid);
521                         } else printf("Call from");
522                         fprintf(f, " (%s)\n", inet_ntoa(iax_get_peer_addr(e->session).sin_addr));
523
524                         if(most_recent_answer) {
525                                 fprintf(f, "Incoming call ignored, there's already a call waiting for answer... \
526 please accept or reject first\n");
527                                 iax_reject(e->session, "Too many calls, we're busy!");
528                         } else {
529                                 if ( !(peer = malloc(sizeof(struct peer)))) {
530                                         fprintf(f, "Warning: Unable to allocate memory!\n");
531                                         return;
532                                 }
533
534                                 peer->time = time(0);
535                                 peer->session = e->session;
536                                 if (peer->gsmin)
537                                         free(peer->gsmin);
538                                 peer->gsmin = 0;
539                                 if (peer->gsmout)
540                                         free(peer->gsmout);
541                                 peer->gsmout = 0;
542
543                                 peer->next = peers;
544                                 peers = peer;
545
546                                 iax_accept(peer->session);
547                                 iax_ring_announce(peer->session);
548                                 most_recent_answer = peer;
549                                 ringing = 1;
550                                 gentone(TONE_RINGER, 0);
551                                 fprintf(f, "Incoming call!\n");
552                         }
553                         issue_prompt(f);
554                 }
555                 iax_event_free(e);
556         }
557 }
558
559 void
560 call(FILE *f, char *num)
561 {
562         struct peer *peer;
563
564         if(!newcall)
565                 newcall = iax_session_new();
566         else {
567                 fprintf(f, "Already attempting to call somewhere, please cancel first!\n");
568                 return;
569         }
570
571         if ( !(peer = malloc(sizeof(struct peer)))) {
572                 fprintf(f, "Warning: Unable to allocate memory!\n");
573                 return;
574         }
575
576         peer->time = time(0);
577         peer->session = newcall;
578         peer->gsmin = 0;
579         peer->gsmout = 0;
580
581         peer->next = peers;
582         peers = peer;
583
584         most_recent_answer = peer;
585
586         offhook = 1;
587         
588         iax_call(peer->session, callerid, num, NULL, 10);
589 }
590
591 void
592 answer_call(void)
593 {
594         if(most_recent_answer)
595                 iax_answer(most_recent_answer->session);
596         printf("Answering call!\n");
597         answered_call = 1;
598         offhook = 1;
599         ringing = 0;
600         gentone(TONE_ANSWER, 1);
601 }
602
603 void
604 reject_call(void)
605 {
606         iax_reject(most_recent_answer->session, "Call rejected manually.");
607         most_recent_answer = 0;
608         ringing = 0;
609         gentone(TONE_NONE, 1);
610 }
611
612 void
613 handle_event(FILE *f, struct iax_event *e, struct peer *p)
614 {
615         short fr[FRAME_SIZE];
616         int len;
617
618         switch(e->etype) {
619                 case IAX_EVENT_HANGUP:
620                         iax_hangup(most_recent_answer->session, "Byeee!");
621                         fprintf(f, "Call disconnected by peer\n");
622                         free(most_recent_answer);
623                         most_recent_answer = 0;
624                         answered_call = 0;
625                         peers = 0;
626                         newcall = 0;
627                         if (offhook)
628                                 gentone(TONE_CONGEST, 0);
629                         break;
630
631                 case IAX_EVENT_REJECT:
632                         fprintf(f, "Authentication was rejected\n");
633                         break;
634                 case IAX_EVENT_ACCEPT:
635                         fprintf(f, "Accepted...\n");
636                         issue_prompt(f);
637                         break;
638                 case IAX_EVENT_RINGA:
639                         fprintf(f, "Ringing...\n");
640                         issue_prompt(f);
641                         gentone(TONE_RINGTONE, 0);
642                         break;
643                 case IAX_EVENT_ANSWER:
644                         answer_call();
645                         gentone(TONE_ANSWER, 1);
646                         break;
647                 case IAX_EVENT_VOICE:
648                         switch(e->event.voice.format) {
649                                 case AST_FORMAT_GSM:
650                                         if(e->event.voice.datalen % 33) {
651                                                 fprintf(stderr, "Weird gsm frame, not a multiple of 33.\n");
652                                                 break;
653                                         }
654
655                                         if (!p->gsmin)
656                                                 p->gsmin = gsm_create();
657
658                                         len = 0;
659                                         while(len < e->event.voice.datalen) {
660                                                 if(gsm_decode(p->gsmin, e->event.voice.data + len, fr)) {
661                                                         fprintf(stderr, "Bad GSM data\n");
662                                                         break;
663                                                 } else {
664                                                         int res;
665
666                                                         res = write(audiofd, fr, sizeof(fr));
667                                                         if (res < 0) 
668                                                                 fprintf(f, "Write failed: %s\n", strerror(errno));
669                                                 }
670                                                 len += 33;
671                                         }
672                                         break;
673                                 default :
674                                         fprintf(f, "Don't know how to handle that format %d\n", e->event.voice.format);
675                         }
676                         break;
677                 default:
678                         fprintf(f, "Unknown event: %d\n", e->etype);
679         }
680 }
681
682 void
683 dump_call(void)
684 {
685         if(most_recent_answer)
686         {
687                 printf("Dumping call!\n");
688             iax_hangup(most_recent_answer->session,"");
689             free(most_recent_answer);
690         }
691         answered_call = 0;
692         most_recent_answer = 0;
693         answered_call = 0;
694         peers = 0;
695         newcall = 0;
696                 offhook = 0;
697                 ringing = 0;
698                 gentone(TONE_NONE, 0);
699 }                                                                               
700
701 void
702 parse_cmd(FILE *f, int argc, char **argv)
703 {
704         if(!strcasecmp(argv[0], "HELP")) {
705                 if(argc == 1)
706                         dump_array(f, help);
707                 else if(argc == 2) {
708                         if(!strcasecmp(argv[1], "HELP"))
709                                 fprintf(f, "Help <Command>\t-\tDisplays general help or specific help on command if supplied an arguement\n");
710                         else if(!strcasecmp(argv[1], "QUIT"))
711                                 fprintf(f, "Quit\t\t-\tShuts down the miniphone\n");
712                         else fprintf(f, "No help available on %s\n", argv[1]);
713                 } else {
714                         fprintf(f, "Too many arguements for command help.\n");
715                 }
716         } else if(!strcasecmp(argv[0], "STATUS")) {
717                 if(argc == 1) {
718                         int c = 0;
719                         struct peer *peerptr = peers;
720
721                         if(!peerptr)
722                                 fprintf(f, "No session matches found.\n");
723                         else while(peerptr) {
724                                 fprintf(f, "Listing sessions:\n\n");
725                                 fprintf(f, "Session %d\n", ++c);
726                                 fprintf(f, "Session existed for %d seconds\n", (int)time(0)-peerptr->time);
727                                 if(answered_call)
728                                         fprintf(f, "Call answered.\n");
729                                 else fprintf(f, "Call ringing.\n");
730
731                                 peerptr = peerptr->next;
732                         }
733                 } else fprintf(f, "Too many arguments for command status.\n");
734         } else if(!strcasecmp(argv[0], "ANSWER")) {
735                 if(argc > 1)
736                         fprintf(f, "Too many arguements for command answer\n");
737                 else answer_call();
738         } else if(!strcasecmp(argv[0], "REJECT")) {
739                 if(argc > 1)
740                         fprintf(f, "Too many arguements for command reject\n");
741                 else {
742                         fprintf(f, "Rejecting current phone call.\n");
743                         reject_call();
744                 }
745         } else if (!strcasecmp(argv[0], "DUMP")) {
746                 dump_call();
747         } else if (!strcasecmp(argv[0], "HANGUP")) {
748                 dump_call();
749         } else if(!strcasecmp(argv[0], "CALL")) {
750                 if(argc > 2)
751                         fprintf(f, "Too many arguements for command call\n");
752                 else {
753                         call(f, argv[1]);
754                 }
755         } else if(!strcasecmp(argv[0], "QUIT")) {
756                 if(argc > 1)
757                         fprintf(f, "Too many arguements for command quit\n");
758                 else {
759                         fprintf(f, "Good bye!\n");
760                         exit(1);
761                 }
762         } else fprintf(f, "Unknown command of %s\n", argv[0]);
763 }
764
765 void
766 issue_prompt(FILE *f)
767 {
768         fprintf(f, "TeleClient> ");
769         fflush(f);
770 }
771
772 void
773 dump_array(FILE *f, char **array) {
774         while(*array)
775                 fprintf(f, "%s\n", *array++);
776 }