]> andersk Git - moira.git/blob - util/imake/imake.c
Initial revision
[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 #ifndef apollo
126 char    *cpp = "/lib/cpp";
127 #else apollo
128 char    *cpp = "/usr/lib/cpp";
129 #endif /* apollo */
130
131 char    *tmpMakefile    = "/usr/tmp/tmp-make.XXXXXX";
132 char    *tmpImakefile    = "/usr/tmp/tmp-imake.XXXXXX";
133 char    *make_argv[ ARGUMENTS ] = { "make" };
134 char    *cpp_argv[ ARGUMENTS ] = {
135         "cpp",
136         "-I.",
137 #ifdef unix
138         "-Uunix",
139 #endif /* unix */
140 #ifdef pegasus
141         "-Dpegasus",
142 #endif /* pegasus */
143 };
144 int     make_argindex;
145 int     cpp_argindex;
146 char    *make = NULL;
147 char    *Imakefile = NULL;
148 char    *Makefile = NULL;
149 char    *Template = "Imake.template";
150 char    *program;
151 char    *FindImakefile();
152 char    *ReadLine();
153 char    *CleanCppInput();
154 char    *strdup();
155
156 boolean verbose = FALSE;
157 boolean show = FALSE;
158 extern int      errno;
159 extern char     *Emalloc();
160 extern char     *realloc();
161 extern char     *getenv();
162 extern char     *mktemp();
163
164 main(argc, argv)
165         int     argc;
166         char    **argv;
167 {
168         FILE    *tmpfd;
169         char    makeMacro[ BUFSIZ ];
170         char    makefileMacro[ BUFSIZ ];
171
172         init();
173         SetOpts(argc, argv);
174
175         AddCppArg("-I/usr/lib/local/imake.includes");
176
177         Imakefile = FindImakefile(Imakefile);
178         if (Makefile)
179                 tmpMakefile = Makefile;
180         else
181                 tmpMakefile = mktemp(strdup(tmpMakefile));
182         AddMakeArg("-f");
183         AddMakeArg( tmpMakefile );
184         sprintf(makeMacro, "MAKE=%s", program);
185         AddMakeArg( makeMacro );
186         sprintf(makefileMacro, "MAKEFILE=%s", Imakefile);
187         AddMakeArg( makefileMacro );
188
189         if ((tmpfd = fopen(tmpMakefile, "w+")) == NULL)
190                 LogFatal("Cannot create temporary file %s.", tmpMakefile);
191
192         cppit(Imakefile, Template, tmpfd);
193
194         if (show) {
195                 if (Makefile == NULL)
196                         showit(tmpfd);
197         } else
198                 makeit();
199         wrapup();
200         exit(0);
201 }
202
203 showit(fd)
204         FILE    *fd;
205 {
206         char    buf[ BUFSIZ ];
207         int     red;
208
209         fseek(fd, 0, 0);
210         while ((red = fread(buf, 1, BUFSIZ, fd)) > 0)
211                 fwrite(buf, red, 1, stdout);
212         if (red < 0)
213                 LogFatal("Cannot write stdout.", "");
214 }
215
216 wrapup()
217 {
218         if (tmpMakefile != Makefile)
219                 unlink(tmpMakefile);
220         unlink(tmpImakefile);
221 }
222
223 catch(sig)
224         int     sig;
225 {
226         errno = 0;
227         LogFatalI("Signal %d.", sig);
228 }
229
230 /*
231  * Initialize some variables.
232  */
233 init()
234 {
235         char    *p;
236
237         make_argindex=0;
238         while (make_argv[ make_argindex ] != NULL)
239                 make_argindex++;
240         cpp_argindex = 0;
241         while (cpp_argv[ cpp_argindex ] != NULL)
242                 cpp_argindex++;
243
244         /*
245          * See if the standard include directory is different than
246          * the default.  Or if cpp is not the default.  Or if the make
247          * found by the PATH variable is not the default.
248          */
249         if (p = getenv("IMAKEINCLUDE")) {
250                 if (*p != '-' || *(p+1) != 'I')
251                         LogFatal("Environment var IMAKEINCLUDE %s\n",
252                                 "must begin with -I");
253                 AddCppArg(p);
254                 for (; *p; p++)
255                         if (*p == ' ') {
256                                 *p++ = '\0';
257                                 AddCppArg(p);
258                         }
259         }
260         if (p = getenv("IMAKECPP"))
261                 cpp = p;
262         if (p = getenv("IMAKEMAKE"))
263                 make = p;
264
265         if (signal(SIGINT, SIG_IGN) != SIG_IGN)
266                 signal(SIGINT, catch);
267 }
268
269 AddMakeArg(arg)
270         char    *arg;
271 {
272         errno = 0;
273         if (make_argindex >= ARGUMENTS-1)
274                 LogFatal("Out of internal storage.", "");
275         make_argv[ make_argindex++ ] = arg;
276         make_argv[ make_argindex ] = NULL;
277 }
278
279 AddCppArg(arg)
280         char    *arg;
281 {
282         errno = 0;
283         if (cpp_argindex >= ARGUMENTS-1)
284                 LogFatal("Out of internal storage.", "");
285         cpp_argv[ cpp_argindex++ ] = arg;
286         cpp_argv[ cpp_argindex ] = NULL;
287 }
288
289 SetOpts(argc, argv)
290         int     argc;
291         char    **argv;
292 {
293         errno = 0;
294         /*
295          * Now gather the arguments for make
296          */
297         program = argv[0];
298         for(argc--, argv++; argc; argc--, argv++) {
299             /*
300              * We intercept these flags.
301              */
302             if (argv[0][0] == '-') {
303                 if (argv[0][1] == 'D') {
304                     AddCppArg(argv[0]);
305                 } else if (argv[0][1] == 'I') {
306                     AddCppArg(argv[0]);
307                 } else if (argv[0][1] == 'f') {
308                     if (argv[0][2])
309                         Imakefile = argv[0]+2;
310                     else {
311                         argc--, argv++;
312                         if (! argc)
313                             LogFatal("No description arg after -f flag\n", "");
314                         Imakefile = argv[0];
315                     }
316                 } else if (argv[0][1] == 's') {
317                     if (argv[0][2])
318                         Makefile = argv[0]+2;
319                     else if (argc > 1 && argv[1][0] != '-') {
320                         argc--, argv++;
321                         Makefile = argv[0];
322                     }
323                     show = TRUE;
324                 } else if (argv[0][1] == 'T') {
325                     if (argv[0][2])
326                         Template = argv[0]+2;
327                     else {
328                         argc--, argv++;
329                         if (! argc)
330                             LogFatal("No description arg after -T flag\n", "");
331                         Template = argv[0];
332                     }
333                 } else if (argv[0][1] == 'v') {
334                     verbose = TRUE;
335                 } else
336                     AddMakeArg(argv[0]);
337             } else
338                 AddMakeArg(argv[0]);
339         }
340 }
341
342 char *FindImakefile(Imakefile)
343         char    *Imakefile;
344 {
345         int     fd;
346
347         if (Imakefile) {
348                 if ((fd = open(Imakefile, O_RDONLY)) < 0)
349                         LogFatal("Cannot open %s.", Imakefile);
350         } else {
351                 if ((fd = open("Imakefile", O_RDONLY)) < 0)
352                         if ((fd = open("imakefile", O_RDONLY)) < 0)
353                                 LogFatal("No description file.", "");
354                         else
355                                 Imakefile = "imakefile";
356                 else
357                         Imakefile = "Imakefile";
358         }
359         close (fd);
360         return(Imakefile);
361 }
362
363 LogFatalI(s, i)
364         char *s;
365         int i;
366 {
367         /*NOSTRICT*/
368         LogFatal(s, (char *)i);
369 }
370
371 LogFatal(x0,x1)
372         char *x0, *x1;
373 {
374         extern char     *sys_errlist[];
375         static boolean  entered = FALSE;
376
377         if (entered)
378                 return;
379         entered = TRUE;
380
381         fprintf(stderr, "%s: ", program);
382         if (errno)
383                 fprintf(stderr, "%s: ", sys_errlist[ errno ]);
384         fprintf(stderr, x0,x1);
385         fprintf(stderr, "  Stop.\n");
386         wrapup();
387         exit(1);
388 }
389
390 showargs(argv)
391         char    **argv;
392 {
393         for (; *argv; argv++)
394                 fprintf(stderr, "%s ", *argv);
395         fprintf(stderr, "\n");
396 }
397
398 cppit(Imakefile, template, outfd)
399         char    *Imakefile;
400         char    *template;
401         FILE    *outfd;
402 {
403         FILE    *pipeFile;
404         int     pid, pipefd[2];
405 #ifdef SYSV
406         int     status;
407 #else   /* !SYSV */
408         union wait      status;
409 #endif  /* !SYSV */
410         char    *cleanedImakefile;
411
412         /*
413          * Get a pipe.
414          */
415         if (pipe(pipefd) < 0)
416                 LogFatal("Cannot make a pipe.", "");
417
418         /*
419          * Fork and exec cpp
420          */
421         pid = vfork();
422         if (pid < 0)
423                 LogFatal("Cannot fork.", "");
424         if (pid) {      /* parent */
425                 close(pipefd[0]);
426                 cleanedImakefile = CleanCppInput(Imakefile);
427                 if ((pipeFile = fdopen(pipefd[1], "w")) == NULL)
428                         LogFatalI("Cannot fdopen fd %d for output.", pipefd[1]);
429                 fprintf(pipeFile, "#define IMAKE_TEMPLATE\t\"%s\"\n",
430                         template);
431                 fprintf(pipeFile, "#define INCLUDE_IMAKEFILE\t\"%s\"\n",
432                         cleanedImakefile);
433                 fprintf(pipeFile, "#include IMAKE_TEMPLATE\n");
434                 fclose(pipeFile);
435                 while (wait(&status) > 0) {
436                         errno = 0;
437 #ifdef SYSV
438                         if ((status >> 8) & 0xff)
439                                 LogFatalI("Signal %d.", (status >> 8) & 0xff);
440                         if (status & 0xff)
441                                 LogFatalI("Exit code %d.", status & 0xff);
442 #else   /* !SYSV */
443                         if (status.w_termsig)
444                                 LogFatalI("Signal %d.", status.w_termsig);
445                         if (status.w_retcode)
446                                 LogFatalI("Exit code %d.", status.w_retcode);
447 #endif  /* !SYSV */
448                 }
449                 CleanCppOutput(outfd);
450         } else {        /* child... dup and exec cpp */
451                 if (verbose)
452                         showargs(cpp_argv);
453                 dup2(pipefd[0], 0);
454                 dup2(fileno(outfd), 1);
455                 close(pipefd[1]);
456                 execv(cpp, cpp_argv);
457                 LogFatal("Cannot exec %s.", cpp);
458         }
459 }
460
461 makeit()
462 {
463         int     pid;
464 #ifdef SYSV
465         int     status;
466 #else   /* !SYSV */
467         union wait      status;
468 #endif  /* !SYSV */
469
470         /*
471          * Fork and exec make
472          */
473         pid = vfork();
474         if (pid < 0)
475                 LogFatal("Cannot fork.", "");
476         if (pid) {      /* parent... simply wait */
477                 while (wait(&status) > 0) {
478                         errno = 0;
479 #ifdef SYSV
480                         if ((status >> 8) & 0xff)
481                                 LogFatalI("Signal %d.", (status >> 8) & 0xff);
482                         if (status & 0xff)
483                                 LogFatalI("Exit code %d.", status & 0xff);
484 #else   /* !SYSV */
485                         if (status.w_termsig)
486                                 LogFatalI("Signal %d.", status.w_termsig);
487                         if (status.w_retcode)
488                                 LogFatalI("Exit code %d.", status.w_retcode);
489 #endif  /* !SYSV */
490                 }
491         } else {        /* child... dup and exec cpp */
492                 if (verbose)
493                         showargs(make_argv);
494                 if (make)
495                         execv(make, make_argv);
496                 else
497                         execvp("make", make_argv);
498                 LogFatal("Cannot exec %s.", cpp);
499         }
500 }
501
502 char *CleanCppInput(Imakefile)
503         char    *Imakefile;
504 {
505         FILE    *outFile = NULL;
506         int     infd;
507         char    *buf,           /* buffer for file content */
508                 *pbuf,          /* walking pointer to buf */
509                 *punwritten,    /* pointer to unwritten portion of buf */
510                 *cleanedImakefile = Imakefile,  /* return value */
511                 *ptoken,        /* pointer to # token */
512                 *pend,          /* pointer to end of # token */
513                 savec;          /* temporary character holder */
514         struct stat     st;
515
516         /*
517          * grab the entire file.
518          */
519         if ((infd = open(Imakefile, O_RDONLY)) < 0)
520                 LogFatal("Cannot open %s for input.", Imakefile);
521         fstat(infd, &st);
522         buf = Emalloc(st.st_size+1);
523         if (read(infd, buf, st.st_size) != st.st_size)
524                 LogFatal("Cannot read all of %s:", Imakefile);
525         close(infd);
526         buf[ st.st_size ] = '\0';
527
528         punwritten = pbuf = buf;
529         while (*pbuf) {
530             /* pad make comments for cpp */
531             if (*pbuf == '#' && (pbuf == buf || pbuf[-1] == '\n')) {
532
533                 ptoken = pbuf+1;
534                 while (*ptoken == ' ' || *ptoken == '\t')
535                         ptoken++;
536                 pend = ptoken;
537                 while (*pend && *pend != ' ' && *pend != '\t' && *pend != '\n')
538                         pend++;
539                 savec = *pend;
540                 *pend = '\0';
541                 if (strcmp(ptoken, "include")
542                  && strcmp(ptoken, "define")
543                  && strcmp(ptoken, "undef")
544                  && strcmp(ptoken, "ifdef")
545                  && strcmp(ptoken, "ifndef")
546                  && strcmp(ptoken, "else")
547                  && strcmp(ptoken, "endif")
548                  && strcmp(ptoken, "if")) {
549                     if (outFile == NULL) {
550                         tmpImakefile = mktemp(strdup(tmpImakefile));
551                         cleanedImakefile = tmpImakefile;
552                         outFile = fopen(tmpImakefile, "w");
553                         if (outFile == NULL)
554                             LogFatal("Cannot open %s for write.\n",
555                                 tmpImakefile);
556                     }
557                     fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
558                     fputs("/**/", outFile);
559                     punwritten = pbuf;
560                 }
561                 *pend = savec;
562             }
563             pbuf++;
564         }
565         if (outFile) {
566             fwrite(punwritten, sizeof(char), pbuf-punwritten, outFile);
567             fclose(outFile); /* also closes the pipe */
568         }
569
570         return(cleanedImakefile);
571 }
572
573 CleanCppOutput(tmpfd)
574         FILE    *tmpfd;
575 {
576         char    *input;
577         int     blankline = 0;
578
579         while(input = ReadLine(tmpfd)) {
580                 if (isempty(input)) {
581                         if (blankline++)
582                                 continue;
583                         KludgeResetRule();
584                 } else {
585                         blankline = 0;
586                         KludgeOutputLine(&input);
587                         fputs(input, tmpfd);
588                 }
589                 putc('\n', tmpfd);
590         }
591         fflush(tmpfd);
592 }
593
594 /*
595  * Determine of a line has nothing in it.  As a side effect, we trim white
596  * space from the end of the line.  Cpp magic cookies are also thrown away.
597  */
598 isempty(line)
599         char    *line;
600 {
601         char    *pend;
602
603         /*
604          * Check for lines of the form
605          *      # n "...
606          * or
607          *      # line n "...
608          */
609         if (*line == '#') {
610                 pend = line+1;
611                 if (*pend == ' ')
612                         pend++;
613                 if (strncmp(pend, "line ", 5) == 0)
614                         pend += 5;
615                 if (isdigit(*pend)) {
616                         while (isdigit(*pend))
617                                 pend++;
618                         if (*pend++ == ' ' && *pend == '"')
619                                 return(TRUE);
620                 }
621         }
622
623         /*
624          * Find the end of the line and then walk back.
625          */
626         for (pend=line; *pend; pend++) ;
627
628         pend--;
629         while (pend >= line && (*pend == ' ' || *pend == '\t'))
630                 pend--;
631         *++pend = '\0';
632         return (*line == '\0');
633 }
634
635 char *ReadLine(tmpfd)
636         FILE    *tmpfd;
637 {
638         static boolean  initialized = FALSE;
639         static char     *buf, *pline, *end;
640         char    *p1, *p2;
641
642         if (! initialized) {
643                 int     total_red;
644                 struct stat     st;
645
646                 /*
647                  * Slurp it all up.
648                  */
649                 fseek(tmpfd, 0, 0);
650                 fstat(fileno(tmpfd), &st);
651                 pline = buf = Emalloc(st.st_size+1);
652                 total_red = read(fileno(tmpfd), buf, st.st_size);
653                 if (total_red != st.st_size)
654                         LogFatal("cannot read %s\n", tmpMakefile);
655                 end = buf + st.st_size;
656                 *end = '\0';
657                 lseek(fileno(tmpfd), 0, 0);
658                 ftruncate(fileno(tmpfd), 0);
659                 initialized = TRUE;
660 #ifdef REDUCED_TO_ASCII_SPACE
661         fprintf(tmpfd, "#\n");
662         fprintf(tmpfd, "# Warning: the cpp used on this machine replaces\n");
663         fprintf(tmpfd, "# all newlines and multiple tabs/spaces in a macro\n");
664         fprintf(tmpfd, "# expansion with a single space.  Imake tries to\n");
665         fprintf(tmpfd, "# compensate for this, but is not always\n");
666         fprintf(tmpfd, "# successful.\n");
667         fprintf(tmpfd, "#\n");
668 #endif /* REDUCED_TO_ASCII_SPACE */
669         }
670
671         for (p1 = pline; p1 < end; p1++) {
672                 if (*p1 == '@' && *(p1+1) == '@') { /* soft EOL */
673                         *p1++ = '\0';
674                         p1++; /* skip over second @ */
675                         break;
676                 }
677                 else if (*p1 == '\n') { /* real EOL */
678                         *p1++ = '\0';
679                         break;
680                 }
681         }
682
683         /*
684          * return NULL at the end of the file.
685          */
686         p2 = (pline == p1 ? NULL : pline);
687         pline = p1;
688         return(p2);
689 }
690
691 writetmpfile(fd, buf, cnt)
692         FILE    *fd;
693         int     cnt;
694         char    *buf;
695 {
696         errno = 0;
697         if (fwrite(buf, cnt, 1, fd) != 1)
698                 LogFatal("Cannot write to %s.", tmpMakefile);
699 }
700
701 char *Emalloc(size)
702         int     size;
703 {
704         char    *p, *malloc();
705
706         if ((p = malloc(size)) == NULL)
707                 LogFatalI("Cannot allocate %d bytes\n", size);
708         return(p);
709 }
710
711 #ifdef REDUCED_TO_ASCII_SPACE
712 KludgeOutputLine(pline)
713         char    **pline;
714 {
715         char    *p = *pline;
716
717         switch (*p) {
718             case '#':   /*Comment - ignore*/
719                 break;
720             case '\t':  /*Already tabbed - ignore it*/
721                 break;
722             case ' ':   /*May need a tab*/
723             default:
724                 while (*p) if (*p++ == ':') {
725                     if (**pline == ' ')
726                         (*pline)++;
727                     InRule = TRUE;
728                     break;
729                 }
730                 if (InRule && **pline == ' ')
731                     **pline = '\t';
732                 break;
733         }
734 }
735
736 KludgeResetRule()
737 {
738         InRule = FALSE;
739 }
740 #endif /* REDUCED_TO_ASCII_SPACE */
741
742 char *strdup(cp)
743         register char *cp;
744 {
745         register char *new = Emalloc(strlen(cp) + 1);
746
747         strcpy(new, cp);
748         return new;
749 }
This page took 0.098082 seconds and 5 git commands to generate.