]> arthur.barton.de Git - netatalk.git/blob - etc/psf/psf.c
c4db9b9988d1e4be0dfc554deeca9eb483061c40
[netatalk.git] / etc / psf / psf.c
1 /*
2  * $Id: psf.c,v 1.9 2005-04-28 20:49:49 bfernhomberg Exp $
3  *
4  * Copyright (c) 1990,1995 Regents of The University of Michigan.
5  * All Rights Reserved. See COPYRIGHT.
6  *
7  * PostScript Filter, psf.
8  *
9  * Handles both PostScript files and text files.  Files with the
10  * '%!' PostScript header are sent directly to the printer,
11  * unmodified.  Text files are first converted to PostScript,
12  * then sent.  Printers may be directly attached or on an AppleTalk
13  * network.  Other media are possible.  Currently, psf invokes
14  * pap to send files to AppleTalk-ed printers.  Replace the pap*
15  * variables to use another program for communication.  psf only
16  * converts plain-text.  If called as "tf" or "df", psf will invoke
17  * a troff or dvi to PostScript converter.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif /* HAVE_CONFIG_H */
23
24 #define FUCKED
25
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif /* HAVE_UNISTD_H */
29 #include <sys/time.h>
30
31 /* POSIX.1 sys/wait.h check */
32 #include <sys/types.h>
33 #ifdef HAVE_SYS_WAIT_H
34 #include <sys/wait.h>
35 #endif /* HAVE_SYS_WAIT_H */
36 #ifndef WEXITSTATUS
37 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
38 #endif /* ! WEXITSTATUS */
39 #ifndef WIFEXITED
40 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
41 #endif /* ! WIFEXITED */
42
43 #include <sys/file.h>
44 #include <syslog.h>
45 #include <atalk/paths.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <ctype.h>
50 #include <signal.h>
51 #include <errno.h>
52
53 /* Forward Declarations */
54 int pexecv(char *path, char *argv[]);
55 int copyio();
56 int textps();
57
58 char            psapath[] = _PATH_PSA;
59 char            *psaargv[] = { "psa", 0, 0, 0, 0 };
60
61 /*
62  * If we're not doing accounting, we just call pap as below.
63  * If we are doing accounting, we call pap twice.  The first time,
64  * we call it with "-E" in arg 2, pagecount.ps in arg 3, and "-" in
65  * arg 4.  The second time, we call it with "-c" in arg 2, pagecount.ps
66  * in arg 3, and 0 in arg 4.
67  */
68 char            pappath[] = _PATH_PAP;
69 char            *papargv[] = { "pap", "-sstatus", 0, 0, 0, 0, 0, 0 };
70
71 char            revpath[] = _PATH_PSORDER;
72 char            *revargv[] = { "psorder", "-d", 0 };
73
74 char            *filtargv[] = { 0, 0, 0 };
75
76 char            inbuf[ 1024 * 8 ];
77 int             inlen;
78
79 FILE            *acctfile = NULL;
80 int             literal;
81 int             width = 80, length = 66, indent = 0;
82 char            *prog, *name, *host;
83
84 struct papersize {
85     int         width;
86     int         length;
87    float        win;
88     float       lin;
89 } papersizes[] = {
90     { 80, 66, 8.5, 11.0 },                      /* US Letter */
91     { 80, 70, 8.27, 11.69 },                    /* A4 */
92 };
93
94 int main( ac, av ) 
95     int         ac;
96     char        **av;
97 {
98     int                 c, rc, children = 0;
99 #ifdef FUCKED
100     int                 psafileno = 0, multiconn = 0, waitidle = 0, waitidle2 = 0;
101 #endif /* FUCKED */
102     int                 status;
103     extern char         *optarg;
104     extern int          optind, opterr;
105
106     opterr = 0;
107     if (( prog = rindex( av[ 0 ], '/' )) == NULL ) {
108         prog = av[ 0 ];
109     } else {
110         prog++;
111     }
112 #ifdef ultrix
113     openlog( prog, LOG_PID );
114 #else /* ultrix */
115     openlog( prog, LOG_PID, LOG_LPR );
116 #endif /* ultrix */
117
118     while (( c = getopt( ac, av, "P:C:D:F:L:J:x:y:n:h:w:l:i:c" )) != EOF ) {
119         switch ( c ) {
120         case 'n' :
121             name = optarg;
122             break;
123
124         case 'h' :
125             host = optarg;
126             break;
127
128         case 'w' :
129             width = atoi( optarg );
130 #ifdef ZEROWIDTH
131             /*
132              * Some version of lpd pass 0 for the page width.
133              */
134             if ( width == 0 ) {
135                 width = 80;
136             }
137 #endif /* ZEROWIDTH */
138             break;
139
140         case 'l' :
141             length = atoi( optarg );
142             break;
143
144         case 'i' :
145             indent = atoi( optarg );
146             break;
147
148         case 'c' :      /* Print control chars */
149             literal++;
150             break;
151
152         case 'F' :
153         case 'L' :
154         case 'J' :
155         case 'P' :
156         case 'x' :
157         case 'y' :
158             break;
159         
160 #ifdef notdef
161         default :
162             syslog( LOG_ERR, "bad option: %c", c );
163             exit( 2 );
164 #endif /* notdef */
165         }
166     }
167     if ( ac - optind > 1 ) {
168         syslog( LOG_ERR, "Too many arguments" );
169         exit( 2 );
170     }
171 #ifdef FUCKED
172     if ( index( prog, 'w' )) {
173         waitidle++;
174     }
175     if ( index( prog, 'W' )) {
176         waitidle2++;
177     }
178     if ( index( prog, 'm' )) {
179         multiconn++;
180     }
181 #endif /* FUCKED */
182
183     syslog( LOG_INFO, "starting for %s", name ? name : "?" );
184
185 restart:
186     if (( inlen = read( 0, inbuf, sizeof( inbuf ))) < 0 ) {
187         syslog( LOG_ERR, "read: %s", strerror(errno) );
188         exit( 1 );
189     }
190     if ( inlen == 0 ) { /* nothing to be done */
191         syslog( LOG_INFO, "done" );
192         exit( 0 );
193     }
194
195     /*
196      * If we've been given an accounting file, start the accounting
197      * process.
198      */
199     if ( optind < ac ) {
200         /* build arguments */
201         psaargv[ 1 ] = av[ optind ];
202         psaargv[ 2 ] = name;
203         psaargv[ 3 ] = host;
204         if (( c = pexecv( psapath, psaargv )) < 0 ) {
205             syslog( LOG_ERR, "%s: %s", psapath, strerror(errno) );
206             exit( 2 );
207         }
208         children++;
209         syslog( LOG_INFO, "accounting with psa[%d]", c );
210     }
211
212     /*
213      * Check prog's name to decide what programs to execute.
214      */
215     if ( strstr( prog, "pap" ) != NULL ) {
216         if ( optind < ac ) {    /* accounting */
217 #ifdef FUCKED
218             if ( multiconn ) {
219                 psafileno = getdtablesize();
220                 psafileno--;
221                 dup2( 1, psafileno );
222
223                 if ( waitidle2 ) {
224                     papargv[ 2 ] = "-W";
225                     papargv[ 3 ] = "-c";
226                     papargv[ 4 ] = "-E";
227                     papargv[ 5 ] = _PATH_PAGECOUNT;
228                     papargv[ 6 ] = "-";
229                     papargv[ 7 ] = 0;
230                 } else if ( waitidle ) {
231                     papargv[ 2 ] = "-w";
232                     papargv[ 3 ] = "-c";
233                     papargv[ 4 ] = "-E";
234                     papargv[ 5 ] = _PATH_PAGECOUNT;
235                     papargv[ 6 ] = "-";
236                     papargv[ 7 ] = 0;
237                 } else {
238                     papargv[ 2 ] = "-c";
239                     papargv[ 3 ] = "-E";
240                     papargv[ 4 ] = _PATH_PAGECOUNT;
241                     papargv[ 5 ] = "-";
242                     papargv[ 6 ] = 0;
243                 }
244             } else {
245                 /*
246                  * This is how it should be done.
247                  */
248                 papargv[ 2 ] = "-c";
249                 papargv[ 3 ] = _PATH_PAGECOUNT;
250                 papargv[ 4 ] = "-";
251                 papargv[ 5 ] = _PATH_PAGECOUNT;
252                 papargv[ 6 ] = 0;
253             }
254 #endif /* FUCKED */
255         } else {
256             papargv[ 2 ] = "-c";
257             papargv[ 3 ] = "-E";
258             papargv[ 4 ] = 0;
259         }
260
261         if (( c = pexecv( pappath, papargv )) < 0 ) {
262             syslog( LOG_ERR, "%s: %s", pappath, strerror(errno) );
263             exit( 2 );
264         }
265         children++;
266         syslog( LOG_INFO, "sending to pap[%d]", c );
267     }
268
269     /*
270      * Might be a good idea to have both a "forw" and a "rev", so that
271      * reversed documents can be reordered for the printing device.
272      */
273     if ( strstr( prog, "rev" ) != NULL ) {
274         if (( c = pexecv( revpath, revargv )) < 0 ) {
275             syslog( LOG_ERR, "%s: %s", revpath, strerror(errno) );
276             exit( 2 );
277         }
278         syslog( LOG_INFO, "sending to rev[%d]", c );
279         children++;
280     }
281
282     /*
283      * Invoke an external (script) filter to produce PostScript from
284      * non-text input.
285      */
286     if ( *prog != 'i' && *prog != 'o' && *( prog + 1 ) == 'f' ) {
287         filtargv[ 0 ] = filtargv[ 1 ] = prog;
288         if (( c = pexecv( _PATH_PSFILTER, filtargv )) < 0 ) {
289             syslog( LOG_ERR, "%s: %s", _PATH_PSFILTER, strerror(errno) );
290             exit( 2 );
291         }
292         syslog( LOG_INFO, "external filter[%d]", c );
293         children++;
294         rc = copyio();          /* external filter */
295     } else {
296         if ( inlen >= 2 && inbuf[ 0 ] == '%' && inbuf[ 1 ] == '!' ) {
297             syslog( LOG_INFO, "PostScript" );
298             rc = copyio();      /* PostScript */
299         } else if ( inlen >= 2 && inbuf[ 0 ] == '\033' && inbuf[ 1 ] == '%' ) {
300             syslog( LOG_INFO, "PostScript w/PJL" );
301             rc = copyio();      /* PostScript */
302         } else {
303             syslog( LOG_INFO, "straight text" );
304             rc = textps();      /* straight text */
305         }
306     }
307
308 #ifdef FUCKED
309     if ( strstr( prog, "pap" ) != NULL && optind < ac && multiconn ) {
310         dup2( psafileno, 1 );
311         close( psafileno );
312         papargv[ 2 ] = "-c";
313         if ( waitidle2 ) {
314             papargv[ 3 ] = "-W";
315             papargv[ 4 ] = _PATH_PAGECOUNT;
316             papargv[ 5 ] = 0;
317         } else if ( waitidle ) {
318             papargv[ 3 ] = "-w";
319             papargv[ 4 ] = _PATH_PAGECOUNT;
320             papargv[ 5 ] = 0;
321         } else {
322             papargv[ 3 ] = _PATH_PAGECOUNT;
323             papargv[ 4 ] = 0;
324         }
325
326         if (( c = pexecv( pappath, papargv )) < 0 ) {
327             syslog( LOG_ERR, "%s: %s", pappath, strerror(errno) );
328             exit( 2 );
329         }
330         children++;
331         syslog( LOG_INFO, "pagecount with pap[%d]", c );
332     }
333 #endif /* FUCKED */
334
335     if ( children ) {
336         close( 1 );
337     }
338     while ( children ) {
339         if (( c = wait3( &status, 0, 0 )) < 0 ) {
340             syslog( LOG_ERR, "wait3: %s", strerror(errno) );
341             exit( 1 );
342         }
343         if ( WIFEXITED( status )) {
344 #ifndef WEXITSTATUS
345 #define WEXITSTATUS(x)  ((x).w_status)
346 #endif /* WEXITSTATUS */
347             if ( WEXITSTATUS( status ) != 0 ) {
348                 syslog( LOG_ERR, "%d died with %d", c, WEXITSTATUS( status ));
349                 exit( WEXITSTATUS( status ));
350             } else {
351                 syslog( LOG_INFO, "%d done", c );
352                 children--;
353             }
354         } else {
355             syslog( LOG_ERR, "%d died badly", c );
356             exit( 1 );
357         }
358     }
359
360     if ( rc == 3 ) {
361         syslog( LOG_INFO, "pausing" );
362         kill( getpid(), SIGSTOP );
363         syslog( LOG_INFO, "restarting" );
364         goto restart;
365     }
366
367     syslog( LOG_INFO, "done" );
368     exit( rc );
369 }
370
371 int copyio()
372 {
373     /* implement the FSM needed to do the suspend. Note that
374      * the last characters will be \031\001 so don't worry
375      * Fun things: 1. \031\001 should not be written to output device
376      *  2. The \031 can be last char of one read, \001 first of next
377      *      - we need to write \031 if not followed by \001
378      */
379     struct timeval      tv;
380     fd_set              fdset;
381     int                 ctl = 0;
382
383 notdone:
384     do {
385         /*
386          * First, \031 and \001 *must* be the last things in the buffer
387          * (\001 can be the first thing in the next buffer).  There's no
388          * need to scan any of the intervening bytes.  Second, if there's
389          * more input, the escape sequence was bogus, and we should keep
390          * reading.
391          */
392         if ( inlen == 1 ) {
393             if ( ctl == 1 ) {
394                 if ( inbuf[ 0 ] == '\001' ) {
395                     ctl = 2;
396                     break;
397                 }
398                 if ( write( 1, "\031", 1 ) != 1 ) {
399                     syslog( LOG_ERR, "write: %s", strerror(errno) );
400                     return( 1 );
401                 }
402                 ctl = 0;
403             }
404
405             if ( inbuf[ 0 ] == '\031' ) {
406                 ctl = 1;
407             }
408
409         } else {
410             if ( ctl == 1 ) {
411                 if ( write( 1, "\031", 1 ) != 1 ) {
412                     syslog( LOG_ERR, "write: %s", strerror(errno) );
413                     return( 1 );
414                 }
415             }
416             ctl = 0;
417             if ( inbuf[ inlen - 2 ] == '\031' &&
418                     inbuf[ inlen - 1 ] == '\001' ) {
419                 ctl = 2;
420             } else if ( inbuf[ inlen - 1 ] == '\031' ) {
421                 ctl = 1;
422             }
423         }
424
425         inlen -= ctl;
426         if (( inlen > 0 ) && ( write( 1, inbuf, inlen ) != inlen )) {
427             syslog( LOG_ERR, "write: %s", strerror(errno) );
428             return( 1 );
429         }
430         if ( ctl == 2 ) {
431             break;
432         }
433     } while (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 );
434
435     if ( ctl == 2 ) {
436         tv.tv_sec = 2;
437         tv.tv_usec = 0;
438         FD_ZERO( &fdset );
439         FD_SET( 0, &fdset );
440         if ( select( 1, &fdset, NULL, NULL, &tv ) != 0 ) {
441             if (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 ) {
442                 goto notdone;
443             }
444         }
445     }
446
447     if ( inlen < 0 ) {
448         syslog( LOG_ERR, "read: %s", strerror(errno) );
449         return( 1 );
450     }
451
452     if ( ctl == 1 ) {
453         if ( write( 1, "\031", 1 ) != 1 ) {
454             syslog( LOG_ERR, "write: %s", strerror(errno) );
455             return( 1 );
456         }
457     } else if ( ctl == 2 ) {
458         return( 3 );
459     }
460     return( 0 );
461 }
462
463 char            *font = "Courier";
464 int             point = 11;
465
466 char            pspro[] = "\
467 /GSV save def                                           % global VM\n\
468 /SP {\n\
469         /SV save def                                    % save vmstate\n\
470         dup /H exch def                                 % save font height\n\
471         exch findfont exch scalefont setfont            % select font\n\
472         ( ) stringwidth pop /W exch def                 % save font width\n\
473         0.5 sub 72 mul /CY exch def                     % save start Y\n\
474         pop 0.5 add 72 mul /CX exch def                 % save start X\n\
475         CX CY moveto                                    % make current point\n\
476 } bind def\n\
477 /S /show load def\n\
478 /NL { CX CY H sub dup /CY exch def moveto } bind def\n\
479 /CR { CX CY moveto } bind def\n\
480 /B { W neg 0 rmoveto}bind def\n\
481 /T { W mul 0 rmoveto}bind def\n\
482 /EP { SV restore showpage } bind def\n\
483 %%EndProlog\n";
484
485 int textps()
486 {
487     struct papersize    papersize;
488     int                 state = 0, line = 0, col = 0, npages = 0, rc;
489     unsigned int        i;
490     char                *p, *end;
491
492 #define elements(x)     (sizeof(x)/sizeof((x)[0]))
493     for ( i = 0; i < elements( papersizes ); i++ ) {
494         if ( width == papersizes[ 0 ].width &&
495                 length == papersizes[ 0 ].length ) {
496             papersize = papersizes[ i ];
497             break;
498         }
499     }
500     if ( i >= elements( papersizes )) {
501         papersize = papersizes[ 0 ];            /* default */
502     }
503
504 #define ST_AVAIL                (1<<0)
505 #define ST_CONTROL              (1<<1)
506 #define ST_PAGE                 (1<<2)
507     /*
508      * convert text lines to postscript.
509      * A grungy little state machine. If I was more creative, I could
510      * probably think of a better way of doing this...
511      */
512     do {
513         p = inbuf;
514         end = inbuf + inlen;
515         while ( p < end ) {
516             if (( state & ST_PAGE ) == 0 && *p != '\031' && *p != '\001' ) {
517                 if ( npages == 0 ) {
518                     printf( "%%!PS-Adobe-2.0\n%%%%Pages: (atend)\n" );
519                     printf( "%%%%DocumentFonts: %s\n", font );
520                     fflush( stdout );
521
522                     /* output postscript prologue: */
523                     if ( write( 1, pspro, sizeof( pspro ) - 1 ) !=
524                             sizeof( pspro ) - 1 ) {
525                         syslog( LOG_ERR, "write prologue: %s", strerror(errno) );
526                         return( 1 );
527                     }
528                     if ( name && host ) {
529                         printf( "statusdict /jobname (%s@%s) put\n", name,
530                                 host );
531                     }
532                 }
533
534                 printf( "%%%%Page: ? %d\n", ++npages );
535                 printf( "%d %f %f /%s %d SP\n", indent,
536                         papersize.win, papersize.lin, font, point );
537                 state |= ST_PAGE;
538             }
539             if ( state & ST_CONTROL && *p != '\001' ) {
540                 /* It is a very bad thing to toss a job because it contains
541                  * unprintable characters.  Instead, we will convert them to
542                  * question marks.  This is adapted from a solution described
543                  * by Werner Eugster <eugster@giub.unibe.ch> on his ApplePrint
544                  * webpage (http://www.giub.unibe.ch/~eugster/appleprint.html).
545                  *
546                  * Note that this is rather ugly code.  The same change is
547                  * applied identically at two different locations in this file.
548                  * It would be better someday to combine the two.
549                 */
550                 if ( !literal ) {
551                         fprintf( stderr,
552                                 "unprintable character (0x%x) converted to ?!\n",
553                                 (unsigned char)*p );
554                         putchar( '?' ); /* Replace unprintable char with a question mark. */
555                 } else {
556                         printf( "\\%o", (unsigned char)031 );
557                 }
558                 state &= ~ST_CONTROL;
559                 col++;
560             }
561
562             switch ( *p ) {
563             case '\n' :         /* end of line */
564                 if ( state & ST_AVAIL ) {
565                     printf( ")S\n" );
566                     state &= ~ST_AVAIL;
567                 }
568                 printf( "NL\n" );
569                 line++;
570                 col = 0;
571                 if ( line >= length ) {
572                     printf( "EP\n" );
573                     state &= ~ST_PAGE;
574                     line = 0;
575                 }
576                 break;
577
578             case '\r' :         /* carriage return (for overtyping) */
579                 if ( state & ST_AVAIL ) {
580                     printf( ")S CR\n" );
581                     state &= ~ST_AVAIL;
582                 }
583                 col = 0;
584                 break;
585
586             case '\f' :         /* form feed */
587                 if ( state & ST_AVAIL ) {
588                     printf( ")S\n" );
589                     state &= ~ST_AVAIL;
590                 }
591                 printf( "EP\n" );
592                 state &= ~ST_PAGE;
593                 line = 0;
594                 col = 0;
595                 break;
596
597             case '\b' :         /* backspace */
598                 /* show line, back up one character */
599                 if ( state & ST_AVAIL ) {
600                     printf( ")S\n" );
601                     state &= ~ST_AVAIL;
602                 }
603                 printf( "B\n" );
604                 col--;
605                 break;
606
607             case '\t' :         /* tab */
608                 if ( state & ST_AVAIL ) {
609                     printf( ")S\n" );
610                     state &= ~ST_AVAIL;
611                 }
612                 printf( "%d T\n", 8 - ( col % 8 ));
613                 col += 8 - ( col % 8 );
614                 break;
615
616             /*
617              * beginning of lpr control sequence
618              */
619             case '\031' :
620                 state |= ST_CONTROL;
621                 break;
622
623             case '\001' :       /* lpr control sequence */
624                 if ( state & ST_CONTROL ) {
625                     rc = 3;
626                     goto out;
627                 }
628                 /* FALLTHROUGH */
629
630             case '\\' :
631             case ')' :
632             case '(' :
633                 if (( state & ST_AVAIL ) == 0 ) {
634                     printf( "(" );
635                     state |= ST_AVAIL;
636                 }
637                 putchar( '\\' );
638                 /* FALLTHROUGH */
639
640             default :
641                 if (( state & ST_AVAIL ) == 0 ) {
642                     printf( "(" );
643                     state |= ST_AVAIL;
644                 }
645                 if ( !isascii( *p ) || !isprint( *p )) {
646                     if ( !literal ) {
647                         fprintf( stderr,
648                             "unprintable character (0x%x) converted to ?!\n",
649                             (unsigned char)*p );
650                         putchar( '?' ); /* Replace unprintable char with a question mark. */
651                     } else {
652                         printf( "\\%o", (unsigned char)*p );
653                     }
654                 } else {
655                     putchar( *p );
656                 }
657                 col++;
658                 break;
659             }
660         p++;
661         }
662     } while (( inlen = read( 0, inbuf, sizeof( inbuf ))) > 0 );
663     if ( inlen < 0 ) {
664         syslog( LOG_ERR, "read: %s", strerror(errno) );
665         return( 1 );
666     }
667     rc = 0;
668
669 out:
670     if ( state & ST_AVAIL ) {
671         printf( ")S\n" );
672         state &= ~ST_AVAIL;
673     }
674
675     if ( state & ST_PAGE ) {
676         printf( "EP\n" );
677         state &= ~ST_PAGE;
678     }
679
680     if ( npages > 0 ) {
681         printf( "%%%%Trailer\nGSV restore\n%%%%Pages: %d\n%%%%EOF\n", npages );
682         fflush( stdout );
683     }
684
685     return( rc );
686 }
687
688 /*
689  * Interface to pipe and exec, for starting children in pipelines.
690  *
691  * Manipulates file descriptors 0, 1, and 2, such that the new child
692  * is reading from the parent's output.
693  */
694 int pexecv( path, argv )
695     char        *path, *argv[];
696 {
697     int         fd[ 2 ], c;
698
699     if ( pipe( fd ) < 0 ) {
700         return( -1 );
701     }
702
703     switch ( c = fork()) {
704     case -1 :
705         return( -1 );
706         /* NOTREACHED */
707
708     case 0 :
709         if ( close( fd[ 1 ] ) < 0 ) {
710             return( -1 );
711         }
712         if ( dup2( fd[ 0 ], 0 ) < 0 ) {
713             return( -1 );
714         }
715         if ( close( fd[ 0 ] ) < 0 ) {
716             return( -1 );
717         }
718         execv( path, argv );
719         return( -1 );
720         /* NOTREACHED */
721
722     default :
723         if ( close( fd[ 0 ] ) < 0 ) {
724             return( -1 );
725         }
726         if ( dup2( fd[ 1 ], 1 ) < 0 ) {
727             return( -1 );
728         }
729         if ( close( fd[ 1 ] ) < 0 ) {
730             return( -1 );
731         }
732         return( c );
733     }
734 }