]> andersk Git - moira.git/blob - util/imake/imake.c
fix spelling mistake in message
[moira.git] / util / imake / imake.c
1 /*
2  * 
3  * Copyright 1985, 1986, 1987 by the Massachusetts Institute of Technology
4  * 
5  * Permission to use, copy, modify, and distribute this
6  * software and its documentation for any purpose and without
7  * fee is hereby granted, provided that the above copyright
8  * notice appear in all copies and that both that copyright
9  * notice and this permission notice appear in supporting
10  * documentation, and that the name of M.I.T. not be used in
11  * advertising or publicity pertaining to distribution of the
12  * software without specific, written prior permission.
13  * M.I.T. makes no representations about the suitability of
14  * this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  * 
17  * $Header$
18  * $Locker$
19  *
20  * Author:
21  *      Todd Brunhoff
22  *      Tektronix, inc.
23  *      While a guest engineer at Project Athena, MIT
24  *
25  * imake: the include-make program.
26  *
27  * Usage: imake [-Idir] [-Ddefine] [-T] [-f imakefile ] [-s] [-v] [make flags]
28  *
29  * Imake takes a template makefile (Imake.template) and runs cpp on it
30  * producing a temporary makefile in /usr/tmp.  It then runs make on
31  * this pre-processed makefile.
32  * Options:
33  *              -D      define.  Same as cpp -D argument.
34  *              -I      Include directory.  Same as cpp -I argument.
35  *              -T      template.  Designate a template other
36  *                      than Imake.template
37  *              -s      show.  Show the produced makefile on the standard
38  *                      output.  Make is not run is this case.  If a file
39  *                      argument is provided, the output is placed there.
40  *              -v      verbose.  Show the make command line executed.
41  *
42  * Environment variables:
43  *              
44  *              IMAKEINCLUDE    Include directory to use in addition to
45  *                              "." and "/usr/lib/local/imake.include".
46  *              IMAKECPP        Cpp to use instead of /lib/cpp
47  *              IMAKEMAKE       make program to use other than what is
48  *                              found by searching the $PATH variable.
49  * Other features:
50  *      imake reads the entire cpp output into memory and then scans it
51  *      for occurences of "@@".  If it encounters them, it replaces it with
52  *      a newline.  It also trims any trailing white space on output lines
53  *      (because make gets upset at them).  This helps when cpp expands
54  *      multi-line macros but you want them to appear on multiple lines.
55  *
56  *      The macros MAKEFILE and MAKE are provided as macros
57  *      to make.  MAKEFILE is set to imake's makefile (not the constructed,
58  *      preprocessed one) and MAKE is set to argv[0], i.e. the name of
59  *      the imake program.
60  *
61  * Theory of operation:
62  *   1. Determine the name of the imakefile from the command line (-f)
63  *      or from the content of the current directory (Imakefile or imakefile).
64  *      Call this <imakefile>.  This gets added to the arguments for
65  *      make as MAKEFILE=<imakefile>.
66  *   2. Determine the name of the template from the command line (-T)
67  *      or the default, Imake.template.  Call this <template>
68  *   3. Start up cpp an provide it with three lines of input:
69  *              #define IMAKE_TEMPLATE          "<template>"
70  *              #define INCLUDE_IMAKEFILE       "<imakefile>"
71  *              #include IMAKE_TEMPLATE
72  *      Note that the define for INCLUDE_IMAKEFILE is intended for
73  *      use in the template file.  This implies that the imake is
74  *      useless unless the template file contains at least the line
75  *              #include INCLUDE_IMAKEFILE
76  *   4. Gather the output from cpp, and clean it up, expanding @@ to
77  *      newlines, stripping trailing white space, cpp control lines,
78  *      and extra blank lines.  This cleaned output is placed in a
79  *      temporary file.  Call this <makefile>.
80  *   5. Start up make specifying <makefile> as its input.
81  *
82  * The design of the template makefile should therefore be:
83  *      <set global macros like CFLAGS, etc.>
84  *      <include machine dependent additions>
85  *      #include INCLUDE_IMAKEFILE
86  *      <add any global targets like 'clean' and long dependencies>
87  */
88 #include        <stdio.h>
89 #include        <ctype.h>
90 #include        <sys/types.h>
91 #include        <sys/file.h>
92 #ifdef SYSV
93 #include        <fcntl.h>
94 #else   /* !SYSV */
95 #include        <sys/wait.h>
96 #endif  /* !SYSV */
97 #include        <sys/signal.h>
98 #include        <sys/stat.h>
99
100 #ifdef SYSV
101 #define dup2(fd1,fd2)   ((fd1 == fd2) ? fd1 : \
102                                 (close(fd2), fcntl(fd1, F_DUPFD, fd2)))
103 #endif  /* SYSV */
104 #define TRUE            1
105 #define FALSE           0
106 #define ARGUMENTS       50
107
108 #if defined(sun) || defined(hpux)
109 #define REDUCED_TO_ASCII_SPACE
110 int     InRule = FALSE;
111 #endif
112
113 /*
114  * Some versions of cpp reduce all tabs in macro expansion to a single
115  * space.  In addition, the escaped newline may be replaced with a
116  * space instead of being deleted.  Blech.
117  */
118 #ifndef REDUCED_TO_ASCII_SPACE
119 #define KludgeOutputLine(arg)
120 #define KludgeResetRule()
121 #endif
122
123 typedef u_char  boolean;
124
125 #include <sys/param.h>
126 #if BSD > 43
127 char    *cpp = "/usr/bin/cpp";
128 #else
129 #ifdef  apollo
130 char    *cpp = "/usr/lib/cpp";
131 #else
132 char    *cpp = "/lib/cpp";
133 #endif
134 #endif
135
136 char    *tmpMakefile    = "/usr/tmp/tmp-make.XXXXXX";
137 char    *tmpImakefile    = "/usr/tmp/tmp-imake.XXXXXX";
138 char    *make_argv[ ARGUMENTS ] = { "make" };
139 char    *cpp_argv[ ARGUMENTS ] = {
140         "cpp",
141         "-I.",
142 #ifdef unix
143         "-Uunix",
144 #endif /* unix */
145 #ifdef pegasus
146         "-Dpegasus",
147 #endif /* pegasus */
148 };
149 int     make_argindex;
150 int     cpp_argindex;
151 char    *make = NULL;
152 char    *Imakefile = NULL;
153 char    *Makefile = NULL;
154 char    *Template = "Imake.template";
155 char    *program;
156 char    *FindImakefile();
157 char    *ReadLine();
158 char    *CleanCppInput();
159 char    *strdup();
160
161 boolean verbose = FALSE;
162 boolean show = FALSE;
163 extern int      errno;
164 extern char     *Emalloc();
165 extern char     *realloc();
166 extern char     *getenv();
167 extern char     *mktemp();
168
169 main(argc, argv)
170         int     argc;
171         char    **argv;
172 {
173         FILE    *tmpfd;
174         char    makeMacro[ BUFSIZ ];
175         char    makefileMacro[ BUFSIZ ];
176
177         init();
178         SetOpts(argc, argv);
179
180         AddCppArg("-I/usr/lib/local/imake.includes");
181
182         Imakefile = FindImakefile(Imakefile);
183         if (Makefile)
184                 tmpMakefile = Makefile;
185         else
186                 tmpMakefile = mktemp(strdup(tmpMakefile));
187         AddMakeArg("-f");
188         AddMakeArg( tmpMakefile );
189         sprintf(makeMacro, "MAKE=%s", program);
190         AddMakeArg( makeMacro );
191         sprintf(makefileMacro, "MAKEFILE=%s", Imakefile);
192         AddMakeArg( makefileMacro );
193
194         if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL)
195                 LogFatal("Cannot create temporary file %s.", tmpMakefile);
196
197         cppit(Imakefile, Template, tmpfd);
198
199         if (show) {
200                 if (Makefile == NULL)
201                         showit(tmpfd);
202         } else
203                 makeit();
204         wrapup();
205         exit(0);
206 }
207
208 showit(fd)
209         FILE    *fd;
210 {
211         char    buf[ BUFSIZ ];
212         int     red;
213
214         fseek(fd, 0, 0);
215         while ((red = fread(buf, 1, BUFSIZ, fd)) > 0)
216                 fwrite(buf, red, 1, stdout);
217         if (red < 0)
218                 LogFatal("Cannot write stdout.", "");
219 }
220
221 wrapup()
222 {
223         if (tmpMakefile != Makefile)
224                 unlink(tmpMakefile);
225         unlink(tmpImakefile);
226 }
227
228 catch(sig)
229         int     sig;
230 {
231         errno = 0;
232         LogFatalI("Signal %d.", sig);
233 }
234
235 /*
236  * Initialize some variables.
237  */
238 init()
239 {
240         char    *p;
241
242         make_argindex=0;
243         while (make_argv[ make_argindex ] != NULL)
244                 make_argindex++;
245         cpp_argindex = 0;
246         while (cpp_argv[ cpp_argindex ] != NULL)
247                 cpp_argindex++;
248
249         /*
250          * See if the standard include directory is different than
251          * the default.  Or if cpp is not the default.  Or if the make
252          * found by the PATH variable is not the default.
253          */
254         if (p = getenv("IMAKEINCLUDE")) {
255                 if (*p != '-' || *(p+1) != 'I')
256                         LogFatal("Environment var IMAKEINCLUDE %s\n",
257                                 "must begin with -I");
258                 AddCppArg(p);
259                 for (; *p; p++)
260                         if (*p == ' ') {
261                                 *p++ = '\0';
262                                 AddCppArg(p);
263                         }
264         }
265         if (p = getenv("IMAKECPP"))
266                 cpp = p;
267         if (p = getenv("IMAKEMAKE"))
268                 make = p;
269
270         if (signal(SIGINT, SIG_IGN) != SIG_IGN)
271                 signal(SIGINT, catch);
272 }
273
274 AddMakeArg(arg)
275         char    *arg;
276 {
277         errno = 0;
278         if (make_argindex >= ARGUMENTS-1)
279                 LogFatal("Out of internal storage.", "");
280         make_argv[ make_argindex++ ] = arg;
281         make_argv[ make_argindex ] = NULL;
282 }
283
284 AddCppArg(arg)
285         char    *arg;
286 {
287         errno = 0;
288         if (cpp_argindex >= ARGUMENTS-1)
289                 LogFatal("Out of internal storage.", "");
290         cpp_argv[ cpp_argindex++ ] = arg;
291         cpp_argv[ cpp_argindex ] = NULL;
292 }
293
294 SetOpts(argc, argv)
295         int     argc;
296         char    **argv;
297 {
298         errno = 0;
299         /*
300          * Now gather the arguments for make
301          */
302         program = argv[0];
303         for(argc--, argv++; argc; argc--, argv++) {
304             /*
305              * We intercept these flags.
306              */
307             if (argv[0][0] == '-') {
308                 if (argv[0][1] == 'D') {
309                     AddCppArg(argv[0]);
310                 } else if (argv[0][1] == 'I') {
311                     AddCppArg(argv[0]);
312                 } else if (argv[0][1] == 'f') {
313                     if (argv[0][2])
314                         Imakefile = argv[0]+2;
315                     else {
316                         argc--, argv++;
317                         if (! argc)
318                             LogFatal("No description arg after -f flag\n", "");
319                         Imakefile = argv[0];
320                     }
321                 } else if (argv[0][1] == 's') {
322                     if (argv[0][2])
323                         Makefile = argv[0]+2;
324                     else if (argc > 1 && argv[1][0] != '-') {
325                         argc--, argv++;
326                         Makefile = argv[0];
327                     }
328                     show = TRUE;
329                 } else if (argv[0][1] == 'T') {
330                     if (argv[0][2])
331                         Template = argv[0]+2;
332                     else {
333                         argc--, argv++;
334                         if (! argc)
335                             LogFatal("No description arg after -T flag\n", "");
336                         Template = argv[0];
337                     }
338                 } else if (argv[0][1] == 'v') {
339                     verbose = TRUE;
340                 } else
341                     AddMakeArg(argv[0]);
342             } else
343                 AddMakeArg(argv[0]);
344         }
345 }
346
347 char *FindImakefile(Imakefile)
348         char    *Imakefile;
349 {
350         int     fd;
351
352         if (Imakefile) {
353                 if ((fd = open(Imakefile, O_RDONLY)) < 0)
354                         LogFatal("Cannot open %s.", Imakefile);
355         } else {
356                 if ((fd = open("Imakefile", O_RDONLY)) < 0)
357                         if ((fd = open("imakefile", O_RDONLY)) < 0)
358                                 LogFatal("No description file.", "");
359                         else
360                                 Imakefile = "imakefile";
361                 else
362                         Imakefile = "Imakefile";
363         }
364         close (fd);
365         return(Imakefile);
366 }
367
368 LogFatalI(s, i)
369         char *s;
370         int i;
371 {
372         /*NOSTRICT*/
373         LogFatal(s, (char *)i);
374 }
375
376 LogFatal(x0,x1)
377         char *x0, *x1;
378 {
379         extern char     *sys_errlist[];
380         static boolean  entered = FALSE;
381
382         if (entered)
383                 return;
384         entered = TRUE;
385
386         fprintf(stderr, "%s: ", program);
387         if (errno)
388                 fprintf(stderr, "%s: ", sys_errlist[ errno ]);
389         fprintf(stderr, x0,x1);
390         fprintf(stderr, "  Stop.\n");
391         wrapup();
392         exit(1);
393 }
394
395 showargs(argv)
396         char    **argv;
397 {
398         for (; *argv; argv++)
399                 fprintf(stderr, "%s ", *argv);
400         fprintf(stderr, "\n");
401 }
402
403 cppit(Imakefile, template, outfd)
404         char    *Imakefile;
405         char    *template;
406         FILE    *outfd;
407 {
408         FILE    *pipeFile;
409         int     pid, pipefd[2];
410 #ifdef SYSV
411         int     status;
412 #else   /* !SYSV */
413         union wait      status;
414 #endif  /* !SYSV */
415         char    *cleanedImakefile;
416
417         /*
418          * Get a pipe.
419          */
420         if (pipe(pipefd) < 0)
421                 LogFatal("Cannot make a pipe.", "");
422
423         /*
424          * Fork and exec cpp
425          */
426         pid = vfork();
427         if (pid < 0)
428                 LogFatal("Cannot fork.", "");
429         if (pid) {      /* parent */
430                 close(pipefd[0]);
431                 cleanedImakefile = CleanCppInput(Imakefile);
432                 if ((pipeFile = fdopen(pipefd[1], "w")) == NULL)
433                         LogFatalI("Cannot fdopen fd %d for output.", pipefd[1]);
434                 fprintf(pipeFile, "#define IMAKE_TEMPLATE\t\"%s\"\n",
435                         template);
436                 fprintf(pipeFile, "#define INCLUDE_IMAKEFILE\t\"%s\"\n",
437                         cleanedImakefile);
438                 fprintf(pipeFile, "#include IMAKE_TEMPLATE\n");
439                 fclose(pipeFile);
440                 while (wait(&status) > 0) {
441                         errno = 0;
442 #ifdef SYSV
443                         if ((status >> 8) & 0xff)
444                                 LogFatalI("Signal %d.", (status >> 8) & 0xff);
445                         if (status & 0xff)
446                                 LogFatalI("Exit code %d.", status & 0xff);
447 #else   /* !SYSV */
448                         if (status.w_termsig)
449                                 LogFatalI("Signal %d.", status.w_termsig);
450                         if (status.w_retcode)
451                                 LogFatalI("Exit code %d.", status.w_retcode);
452 #endif  /* !SYSV */
453                 }
454                 CleanCppOutput(outfd);
455         } else {        /* child... dup and exec cpp */
456                 if (verbose)
457                         showargs(cpp_argv);
458                 dup2(pipefd[0], 0);
459                 dup2(fileno(outfd), 1);
460                 close(pipefd[1]);
461                 execv(cpp, cpp_argv);
462                 LogFatal("Cannot exec %s.", cpp);
463         }
464 }
465
466 makeit()
467 {
468         int     pid;
469 #ifdef SYSV
470         int     status;
471 #else   /* !SYSV */
472         union wait      status;
473 #endif  /* !SYSV */
474
475         /*
476          * Fork and exec make
477          */
478         pid = vfork();
479         if (pid < 0)
480                 LogFatal("Cannot fork.", "");
481         if (pid) {      /* parent... simply wait */
482                 while (wait(&status) > 0) {
483                         errno = 0;
484 #ifdef SYSV
485                         if ((status >> 8) & 0xff)
486                                 LogFatalI("Signal %d.", (status >> 8) & 0xff);
487                         if (status & 0xff)
488                                 LogFatalI("Exit code %d.", status & 0xff);
489 #else   /* !SYSV */
490                         if (status.w_termsig)
491                                 LogFatalI("Signal %d.", status.w_termsig);
492                         if (status.w_retcode)
493                                 LogFatalI("Exit code %d.", status.w_retcode);
494 #endif  /* !SYSV */
495                 }
496         } else {        /* child... dup and exec cpp */
497                 if (verbose)
498                         showargs(make_argv);
499                 if (make)
500                         execv(make, make_argv);
501                 else
502                         execvp("make", make_argv);
503                 LogFatal("Cannot exec %s.", cpp);
504         }
505 }
506
507 char *CleanCppInput(Imakefile)
508         char    *Imakefile;
509 {
510         FILE    *outFile = NULL;
511         int     infd;
512         char    *buf,           /* buffer for file content */
513                 *pbuf,          /* walking pointer to buf */
514                 *punwritten,    /* pointer to unwritten portion of buf */
515                 *cleanedImakefile = Imakefile,  /* return value */
516                 *ptoken,        /* pointer to # token */
517                 *pend,          /* pointer to end of # token */
518                 savec;          /* temporary character holder */
519         struct stat     st;
520
521         /*
522          * grab the entire file.
523          */
524         if ((infd = open(Imakefile, O_RDONLY)) < 0)
525                 LogFatal("Cannot open %s for input.", Imakefile);
526         fstat(infd, &st);
527         buf = Emalloc(st.st_size+1);
528         if (read(infd, buf, st.st_size) != st.st_size)
529                 LogFatal("Cannot read all of %s:", Imakefile);
530         close(infd);
531         buf[ st.st_size ] = '\0';
532
533         punwritten = pbuf = buf;
534         while (*pbuf) {
535             /* pad make comments for cpp */
536             if (*pbuf == '#' && (pbuf == buf || pbuf[-1] == '\n')) {
537
538                 ptoken = pbuf+1;
539                 while (*ptoken == ' ' || *ptoken == '\t')
540                         ptoken++;
541                 pend = ptoken;
542                 while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
543                         pend++;
544                 savec = *pend;
545                 *pend = '\0';
546                 if (strcmp(ptoken, "include")
547                  && strcmp(ptoken, "define")
548                  && strcmp(ptoken, "undef")
549                  && strcmp(ptoken, "ifdef")
550                  && strcmp(ptoken, "ifndef")
551                  && strcmp(ptoken, "else")
552                  && strcmp(ptoken, "endif")
553                  && strcmp(ptoken, "if")) {
554                     if (outFile == NULL) {
555                         tmpImakefile = mktemp(strdup(tmpImakefile));
556                         cleanedImakefile = tmpImakefile;
557                         outFile = fopen(tmpImakefile, "w");
558                         if (outFile == NULL)
559                             LogFatal("Cannot open %s for write.\n",
560                                 tmpImakefile);
561                     }
562                     fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
563                     fputs("/**/", outFile);
564                     punwritten = pbuf;
565                 }
566                 *pend = savec;
567             }
568             pbuf++;
569         }
570         if (outFile) {
571             fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
572             fclose(outFile); /* also closes the pipe */
573         }
574
575         return(cleanedImakefile);
576 }
577
578 CleanCppOutput(tmpfd)
579         FILE    *tmpfd;
580 {
581         char    *input;
582         int     blankline = 0;
583
584         while(input = ReadLine(tmpfd)) {
585                 if (isempty(input)) {
586                         if (blankline++)
587                                 continue;
588                         KludgeResetRule();
589                 } else {
590                         blankline = 0;
591                         KludgeOutputLine(&input);
592                         fputs(input, tmpfd);
593                 }
594                 putc('\n', tmpfd);
595         }
596         fflush(tmpfd);
597 }
598
599 /*
600  * Determine of a line has nothing in it.  As a side effect, we trim white
601  * space from the end of the line.  Cpp magic cookies are also thrown away.
602  */
603 isempty(line)
604         char    *line;
605 {
606         char    *pend;
607
608         /*
609          * Check for lines of the form
610          *      # n "...
611          * or
612          *      # line n "...
613          */
614         if (*line == '#') {
615                 pend = line+1;
616                 if (*pend == ' ')
617                         pend++;
618                 if (strncmp(pend, "line ", 5) == 0)
619                         pend += 5;
620                 if (isdigit(*pend)) {
621                         while (isdigit(*pend))
622                                 pend++;
623                         if (*pend++ == ' ' && *pend == '"')
624                                 return(TRUE);
625                 }
626         }
627
628         /*
629          * Find the end of the line and then walk back.
630          */
631         for (pend=line; *pend; pend++) ;
632
633         pend--;
634         while (pend >= line && (*pend == ' ' || *pend == '\t'))
635                 pend--;
636         *++pend = '\0';
637         return (*line == '\0');
638 }
639
640 char *ReadLine(tmpfd)
641         FILE    *tmpfd;
642 {
643         static boolean  initialized = FALSE;
644         static char     *buf, *pline, *end;
645         char    *p1, *p2;
646
647         if (! initialized) {
648                 int     total_red;
649                 struct stat     st;
650
651                 /*
652                  * Slurp it all up.
653                  */
654                 fseek(tmpfd, 0, 0);
655                 fstat(fileno(tmpfd), &st);
656                 pline = buf = Emalloc(st.st_size+1);
657                 total_red = read(fileno(tmpfd), buf, st.st_size);
658                 if (total_red != st.st_size)
659                         LogFatal("cannot read %s\n", tmpMakefile);
660                 end = buf + st.st_size;
661                 *end = '\0';
662                 lseek(fileno(tmpfd), 0, 0);
663                 ftruncate(fileno(tmpfd), 0);
664                 initialized = TRUE;
665 #ifdef REDUCED_TO_ASCII_SPACE
666         fprintf(tmpfd, "#\n");
667         fprintf(tmpfd, "# Warning: the cpp used on this machine replaces\n");
668         fprintf(tmpfd, "# all newlines and multiple tabs/spaces in a macro\n");
669         fprintf(tmpfd, "# expansion with a single space.  Imake tries to\n");
670         fprintf(tmpfd, "# compensate for this, but is not always\n");
671         fprintf(tmpfd, "# successful.\n");
672         fprintf(tmpfd, "#\n");
673 #endif /* REDUCED_TO_ASCII_SPACE */
674         }
675
676         for (p1 = pline; p1 < end; p1++) {
677                 if (*p1 == '@' && *(p1+1) == '@') { /* soft EOL */
678                         *p1++ = '\0';
679                         p1++; /* skip over second @ */
680                         break;
681                 }
682                 else if (*p1 == '\n') { /* real EOL */
683                         *p1++ = '\0';
684                         break;
685                 }
686         }
687
688         /*
689          * return NULL at the end of the file.
690          */
691         p2 = (pline == p1 ? NULL : pline);
692         pline = p1;
693         return(p2);
694 }
695
696 writetmpfile(fd, buf, cnt)
697         FILE    *fd;
698         int     cnt;
699         char    *buf;
700 {
701         errno = 0;
702         if (fwrite(buf, cnt, 1, fd) != 1)
703                 LogFatal("Cannot write to %s.", tmpMakefile);
704 }
705
706 char *Emalloc(size)
707         int     size;
708 {
709         char    *p, *malloc();
710
711         if ((p = malloc(size)) == NULL)
712                 LogFatalI("Cannot allocate %d bytes\n", size);
713         return(p);
714 }
715
716 #ifdef REDUCED_TO_ASCII_SPACE
717 KludgeOutputLine(pline)
718         char    **pline;
719 {
720         char    *p = *pline;
721
722         switch (*p) {
723             case '#':   /*Comment - ignore*/
724                 break;
725             case '\t':  /*Already tabbed - ignore it*/
726                 break;
727             case ' ':   /*May need a tab*/
728             default:
729                 while (*p) if (*p++ == ':') {
730                     if (**pline == ' ')
731                         (*pline)++;
732                     InRule = TRUE;
733                     break;
734                 }
735                 if (InRule && **pline == ' ')
736                     **pline = '\t';
737                 break;
738         }
739 }
740
741 KludgeResetRule()
742 {
743         InRule = FALSE;
744 }
745 #endif /* REDUCED_TO_ASCII_SPACE */
746
747 char *strdup(cp)
748         register char *cp;
749 {
750         register char *new = Emalloc(strlen(cp) + 1);
751
752         strcpy(new, cp);
753         return new;
754 }
This page took 0.503508 seconds and 5 git commands to generate.