daily update
[binutils.git] / binutils / nlmheader.y
blob3df603d009850cf107ce56578b3ff7a728902028
1 %{/* nlmheader.y - parse NLM header specification keywords.
2 Copyright 1993, 1994, 1995, 1997, 1998, 2001, 2002
3 Free Software Foundation, Inc.
5 This file is part of GNU Binutils.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21 /* Written by Ian Lance Taylor <ian@cygnus.com>.
23 This bison file parses the commands recognized by the NetWare NLM
24 linker, except for lists of object files. It stores the
25 information in global variables.
27 This implementation is based on the description in the NetWare Tool
28 Maker Specification manual, edition 1.0. */
30 #include <ansidecl.h>
31 #include <stdio.h>
32 #include "safe-ctype.h"
33 #include "bfd.h"
34 #include "bucomm.h"
35 #include "nlm/common.h"
36 #include "nlm/internal.h"
37 #include "nlmconv.h"
39 /* Information is stored in the structures pointed to by these
40 variables. */
42 Nlm_Internal_Fixed_Header *fixed_hdr;
43 Nlm_Internal_Variable_Header *var_hdr;
44 Nlm_Internal_Version_Header *version_hdr;
45 Nlm_Internal_Copyright_Header *copyright_hdr;
46 Nlm_Internal_Extended_Header *extended_hdr;
48 /* Procedure named by CHECK. */
49 char *check_procedure;
50 /* File named by CUSTOM. */
51 char *custom_file;
52 /* Whether to generate debugging information (DEBUG). */
53 boolean debug_info;
54 /* Procedure named by EXIT. */
55 char *exit_procedure;
56 /* Exported symbols (EXPORT). */
57 struct string_list *export_symbols;
58 /* List of files from INPUT. */
59 struct string_list *input_files;
60 /* Map file name (MAP, FULLMAP). */
61 char *map_file;
62 /* Whether a full map has been requested (FULLMAP). */
63 boolean full_map;
64 /* File named by HELP. */
65 char *help_file;
66 /* Imported symbols (IMPORT). */
67 struct string_list *import_symbols;
68 /* File named by MESSAGES. */
69 char *message_file;
70 /* Autoload module list (MODULE). */
71 struct string_list *modules;
72 /* File named by OUTPUT. */
73 char *output_file;
74 /* File named by SHARELIB. */
75 char *sharelib_file;
76 /* Start procedure name (START). */
77 char *start_procedure;
78 /* VERBOSE. */
79 boolean verbose;
80 /* RPC description file (XDCDATA). */
81 char *rpc_file;
83 /* The number of serious errors that have occurred. */
84 int parse_errors;
86 /* The current symbol prefix when reading a list of import or export
87 symbols. */
88 static char *symbol_prefix;
90 /* Parser error message handler. */
91 #define yyerror(msg) nlmheader_error (msg);
93 /* Local functions. */
94 static int yylex PARAMS ((void));
95 static void nlmlex_file_push PARAMS ((const char *));
96 static boolean nlmlex_file_open PARAMS ((const char *));
97 static int nlmlex_buf_init PARAMS ((void));
98 static char nlmlex_buf_add PARAMS ((int));
99 static long nlmlex_get_number PARAMS ((const char *));
100 static void nlmheader_identify PARAMS ((void));
101 static void nlmheader_warn PARAMS ((const char *, int));
102 static void nlmheader_error PARAMS ((const char *));
103 static struct string_list * string_list_cons PARAMS ((char *,
104 struct string_list *));
105 static struct string_list * string_list_append PARAMS ((struct string_list *,
106 struct string_list *));
107 static struct string_list * string_list_append1 PARAMS ((struct string_list *,
108 char *));
109 static char *xstrdup PARAMS ((const char *));
113 %union
115 char *string;
116 struct string_list *list;
119 /* The reserved words. */
121 %token CHECK CODESTART COPYRIGHT CUSTOM DATE DEBUG DESCRIPTION EXIT
122 %token EXPORT FLAG_ON FLAG_OFF FULLMAP HELP IMPORT INPUT MAP MESSAGES
123 %token MODULE MULTIPLE OS_DOMAIN OUTPUT PSEUDOPREEMPTION REENTRANT
124 %token SCREENNAME SHARELIB STACK START SYNCHRONIZE
125 %token THREADNAME TYPE VERBOSE VERSIONK XDCDATA
127 /* Arguments. */
129 %token <string> STRING
130 %token <string> QUOTED_STRING
132 /* Typed non-terminals. */
133 %type <list> symbol_list_opt symbol_list string_list
134 %type <string> symbol
138 /* Keywords must start in the leftmost column of the file. Arguments
139 may appear anywhere else. The lexer uses this to determine what
140 token to return, so we don't have to worry about it here. */
142 /* The entire file is just a list of commands. */
144 file:
145 commands
148 /* A possibly empty list of commands. */
150 commands:
151 /* May be empty. */
152 | command commands
155 /* A single command. There is where most of the work takes place. */
157 command:
158 CHECK STRING
160 check_procedure = $2;
162 | CODESTART STRING
164 nlmheader_warn (_("CODESTART is not implemented; sorry"), -1);
165 free ($2);
167 | COPYRIGHT QUOTED_STRING
169 int len;
171 strncpy (copyright_hdr->stamp, "CoPyRiGhT=", 10);
172 len = strlen ($2);
173 if (len >= NLM_MAX_COPYRIGHT_MESSAGE_LENGTH)
175 nlmheader_warn (_("copyright string is too long"),
176 NLM_MAX_COPYRIGHT_MESSAGE_LENGTH - 1);
177 len = NLM_MAX_COPYRIGHT_MESSAGE_LENGTH - 1;
179 copyright_hdr->copyrightMessageLength = len;
180 strncpy (copyright_hdr->copyrightMessage, $2, len);
181 copyright_hdr->copyrightMessage[len] = '\0';
182 free ($2);
184 | CUSTOM STRING
186 custom_file = $2;
188 | DATE STRING STRING STRING
190 /* We don't set the version stamp here, because we use the
191 version stamp to detect whether the required VERSION
192 keyword was given. */
193 version_hdr->month = nlmlex_get_number ($2);
194 version_hdr->day = nlmlex_get_number ($3);
195 version_hdr->year = nlmlex_get_number ($4);
196 free ($2);
197 free ($3);
198 free ($4);
199 if (version_hdr->month < 1 || version_hdr->month > 12)
200 nlmheader_warn (_("illegal month"), -1);
201 if (version_hdr->day < 1 || version_hdr->day > 31)
202 nlmheader_warn (_("illegal day"), -1);
203 if (version_hdr->year < 1900 || version_hdr->year > 3000)
204 nlmheader_warn (_("illegal year"), -1);
206 | DEBUG
208 debug_info = true;
210 | DESCRIPTION QUOTED_STRING
212 int len;
214 len = strlen ($2);
215 if (len > NLM_MAX_DESCRIPTION_LENGTH)
217 nlmheader_warn (_("description string is too long"),
218 NLM_MAX_DESCRIPTION_LENGTH);
219 len = NLM_MAX_DESCRIPTION_LENGTH;
221 var_hdr->descriptionLength = len;
222 strncpy (var_hdr->descriptionText, $2, len);
223 var_hdr->descriptionText[len] = '\0';
224 free ($2);
226 | EXIT STRING
228 exit_procedure = $2;
230 | EXPORT
232 symbol_prefix = NULL;
234 symbol_list_opt
236 export_symbols = string_list_append (export_symbols, $3);
238 | FLAG_ON STRING
240 fixed_hdr->flags |= nlmlex_get_number ($2);
241 free ($2);
243 | FLAG_OFF STRING
245 fixed_hdr->flags &=~ nlmlex_get_number ($2);
246 free ($2);
248 | FULLMAP
250 map_file = "";
251 full_map = true;
253 | FULLMAP STRING
255 map_file = $2;
256 full_map = true;
258 | HELP STRING
260 help_file = $2;
262 | IMPORT
264 symbol_prefix = NULL;
266 symbol_list_opt
268 import_symbols = string_list_append (import_symbols, $3);
270 | INPUT string_list
272 input_files = string_list_append (input_files, $2);
274 | MAP
276 map_file = "";
278 | MAP STRING
280 map_file = $2;
282 | MESSAGES STRING
284 message_file = $2;
286 | MODULE string_list
288 modules = string_list_append (modules, $2);
290 | MULTIPLE
292 fixed_hdr->flags |= 0x2;
294 | OS_DOMAIN
296 fixed_hdr->flags |= 0x10;
298 | OUTPUT STRING
300 if (output_file == NULL)
301 output_file = $2;
302 else
303 nlmheader_warn (_("ignoring duplicate OUTPUT statement"), -1);
305 | PSEUDOPREEMPTION
307 fixed_hdr->flags |= 0x8;
309 | REENTRANT
311 fixed_hdr->flags |= 0x1;
313 | SCREENNAME QUOTED_STRING
315 int len;
317 len = strlen ($2);
318 if (len >= NLM_MAX_SCREEN_NAME_LENGTH)
320 nlmheader_warn (_("screen name is too long"),
321 NLM_MAX_SCREEN_NAME_LENGTH);
322 len = NLM_MAX_SCREEN_NAME_LENGTH;
324 var_hdr->screenNameLength = len;
325 strncpy (var_hdr->screenName, $2, len);
326 var_hdr->screenName[NLM_MAX_SCREEN_NAME_LENGTH] = '\0';
327 free ($2);
329 | SHARELIB STRING
331 sharelib_file = $2;
333 | STACK STRING
335 var_hdr->stackSize = nlmlex_get_number ($2);
336 free ($2);
338 | START STRING
340 start_procedure = $2;
342 | SYNCHRONIZE
344 fixed_hdr->flags |= 0x4;
346 | THREADNAME QUOTED_STRING
348 int len;
350 len = strlen ($2);
351 if (len >= NLM_MAX_THREAD_NAME_LENGTH)
353 nlmheader_warn (_("thread name is too long"),
354 NLM_MAX_THREAD_NAME_LENGTH);
355 len = NLM_MAX_THREAD_NAME_LENGTH;
357 var_hdr->threadNameLength = len;
358 strncpy (var_hdr->threadName, $2, len);
359 var_hdr->threadName[len] = '\0';
360 free ($2);
362 | TYPE STRING
364 fixed_hdr->moduleType = nlmlex_get_number ($2);
365 free ($2);
367 | VERBOSE
369 verbose = true;
371 | VERSIONK STRING STRING STRING
373 long val;
375 strncpy (version_hdr->stamp, "VeRsIoN#", 8);
376 version_hdr->majorVersion = nlmlex_get_number ($2);
377 val = nlmlex_get_number ($3);
378 if (val < 0 || val > 99)
379 nlmheader_warn (_("illegal minor version number (must be between 0 and 99)"),
380 -1);
381 else
382 version_hdr->minorVersion = val;
383 val = nlmlex_get_number ($4);
384 if (val < 0)
385 nlmheader_warn (_("illegal revision number (must be between 0 and 26)"),
386 -1);
387 else if (val > 26)
388 version_hdr->revision = 0;
389 else
390 version_hdr->revision = val;
391 free ($2);
392 free ($3);
393 free ($4);
395 | VERSIONK STRING STRING
397 long val;
399 strncpy (version_hdr->stamp, "VeRsIoN#", 8);
400 version_hdr->majorVersion = nlmlex_get_number ($2);
401 val = nlmlex_get_number ($3);
402 if (val < 0 || val > 99)
403 nlmheader_warn (_("illegal minor version number (must be between 0 and 99)"),
404 -1);
405 else
406 version_hdr->minorVersion = val;
407 version_hdr->revision = 0;
408 free ($2);
409 free ($3);
411 | XDCDATA STRING
413 rpc_file = $2;
417 /* A possibly empty list of symbols. */
419 symbol_list_opt:
420 /* Empty. */
422 $$ = NULL;
424 | symbol_list
426 $$ = $1;
430 /* A list of symbols in an import or export list. Prefixes may appear
431 in parentheses. We need to use left recursion here to avoid
432 building up a large import list on the parser stack. */
434 symbol_list:
435 symbol
437 $$ = string_list_cons ($1, NULL);
439 | symbol_prefix
441 $$ = NULL;
443 | symbol_list symbol
445 $$ = string_list_append1 ($1, $2);
447 | symbol_list symbol_prefix
449 $$ = $1;
453 /* A prefix for subsequent symbols. */
455 symbol_prefix:
456 '(' STRING ')'
458 if (symbol_prefix != NULL)
459 free (symbol_prefix);
460 symbol_prefix = $2;
464 /* A single symbol. */
466 symbol:
467 STRING
469 if (symbol_prefix == NULL)
470 $$ = $1;
471 else
473 $$ = xmalloc (strlen (symbol_prefix) + strlen ($1) + 2);
474 sprintf ($$, "%s@%s", symbol_prefix, $1);
475 free ($1);
480 /* A list of strings. */
482 string_list:
483 /* May be empty. */
485 $$ = NULL;
487 | STRING string_list
489 $$ = string_list_cons ($1, $2);
495 /* If strerror is just a macro, we want to use the one from libiberty
496 since it will handle undefined values. */
497 #undef strerror
498 extern char *strerror PARAMS ((int));
500 /* The lexer is simple, too simple for flex. Keywords are only
501 recognized at the start of lines. Everything else must be an
502 argument. A comma is treated as whitespace. */
504 /* The states the lexer can be in. */
506 enum lex_state
508 /* At the beginning of a line. */
509 BEGINNING_OF_LINE,
510 /* In the middle of a line. */
511 IN_LINE
514 /* We need to keep a stack of files to handle file inclusion. */
516 struct input
518 /* The file to read from. */
519 FILE *file;
520 /* The name of the file. */
521 char *name;
522 /* The current line number. */
523 int lineno;
524 /* The current state. */
525 enum lex_state state;
526 /* The next file on the stack. */
527 struct input *next;
530 /* The current input file. */
532 static struct input current;
534 /* The character which introduces comments. */
535 #define COMMENT_CHAR '#'
537 /* Start the lexer going on the main input file. */
539 boolean
540 nlmlex_file (name)
541 const char *name;
543 current.next = NULL;
544 return nlmlex_file_open (name);
547 /* Start the lexer going on a subsidiary input file. */
549 static void
550 nlmlex_file_push (name)
551 const char *name;
553 struct input *push;
555 push = (struct input *) xmalloc (sizeof (struct input));
556 *push = current;
557 if (nlmlex_file_open (name))
558 current.next = push;
559 else
561 current = *push;
562 free (push);
566 /* Start lexing from a file. */
568 static boolean
569 nlmlex_file_open (name)
570 const char *name;
572 current.file = fopen (name, "r");
573 if (current.file == NULL)
575 fprintf (stderr, "%s:%s: %s\n", program_name, name, strerror (errno));
576 ++parse_errors;
577 return false;
579 current.name = xstrdup (name);
580 current.lineno = 1;
581 current.state = BEGINNING_OF_LINE;
582 return true;
585 /* Table used to turn keywords into tokens. */
587 struct keyword_tokens_struct
589 const char *keyword;
590 int token;
593 struct keyword_tokens_struct keyword_tokens[] =
595 { "CHECK", CHECK },
596 { "CODESTART", CODESTART },
597 { "COPYRIGHT", COPYRIGHT },
598 { "CUSTOM", CUSTOM },
599 { "DATE", DATE },
600 { "DEBUG", DEBUG },
601 { "DESCRIPTION", DESCRIPTION },
602 { "EXIT", EXIT },
603 { "EXPORT", EXPORT },
604 { "FLAG_ON", FLAG_ON },
605 { "FLAG_OFF", FLAG_OFF },
606 { "FULLMAP", FULLMAP },
607 { "HELP", HELP },
608 { "IMPORT", IMPORT },
609 { "INPUT", INPUT },
610 { "MAP", MAP },
611 { "MESSAGES", MESSAGES },
612 { "MODULE", MODULE },
613 { "MULTIPLE", MULTIPLE },
614 { "OS_DOMAIN", OS_DOMAIN },
615 { "OUTPUT", OUTPUT },
616 { "PSEUDOPREEMPTION", PSEUDOPREEMPTION },
617 { "REENTRANT", REENTRANT },
618 { "SCREENNAME", SCREENNAME },
619 { "SHARELIB", SHARELIB },
620 { "STACK", STACK },
621 { "STACKSIZE", STACK },
622 { "START", START },
623 { "SYNCHRONIZE", SYNCHRONIZE },
624 { "THREADNAME", THREADNAME },
625 { "TYPE", TYPE },
626 { "VERBOSE", VERBOSE },
627 { "VERSION", VERSIONK },
628 { "XDCDATA", XDCDATA }
631 #define KEYWORD_COUNT (sizeof (keyword_tokens) / sizeof (keyword_tokens[0]))
633 /* The lexer accumulates strings in these variables. */
634 static char *lex_buf;
635 static int lex_size;
636 static int lex_pos;
638 /* Start accumulating strings into the buffer. */
639 #define BUF_INIT() \
640 ((void) (lex_buf != NULL ? lex_pos = 0 : nlmlex_buf_init ()))
642 static int
643 nlmlex_buf_init ()
645 lex_size = 10;
646 lex_buf = xmalloc (lex_size + 1);
647 lex_pos = 0;
648 return 0;
651 /* Finish a string in the buffer. */
652 #define BUF_FINISH() ((void) (lex_buf[lex_pos] = '\0'))
654 /* Accumulate a character into the buffer. */
655 #define BUF_ADD(c) \
656 ((void) (lex_pos < lex_size \
657 ? lex_buf[lex_pos++] = (c) \
658 : nlmlex_buf_add (c)))
660 static char
661 nlmlex_buf_add (c)
662 int c;
664 if (lex_pos >= lex_size)
666 lex_size *= 2;
667 lex_buf = xrealloc (lex_buf, lex_size + 1);
670 return lex_buf[lex_pos++] = c;
673 /* The lexer proper. This is called by the bison generated parsing
674 code. */
676 static int
677 yylex ()
679 int c;
681 tail_recurse:
683 c = getc (current.file);
685 /* Commas are treated as whitespace characters. */
686 while (ISSPACE (c) || c == ',')
688 current.state = IN_LINE;
689 if (c == '\n')
691 ++current.lineno;
692 current.state = BEGINNING_OF_LINE;
694 c = getc (current.file);
697 /* At the end of the file we either pop to the previous file or
698 finish up. */
699 if (c == EOF)
701 fclose (current.file);
702 free (current.name);
703 if (current.next == NULL)
704 return 0;
705 else
707 struct input *next;
709 next = current.next;
710 current = *next;
711 free (next);
712 goto tail_recurse;
716 /* A comment character always means to drop everything until the
717 next newline. */
718 if (c == COMMENT_CHAR)
722 c = getc (current.file);
724 while (c != '\n');
725 ++current.lineno;
726 current.state = BEGINNING_OF_LINE;
727 goto tail_recurse;
730 /* An '@' introduces an include file. */
731 if (c == '@')
735 c = getc (current.file);
736 if (c == '\n')
737 ++current.lineno;
739 while (ISSPACE (c));
740 BUF_INIT ();
741 while (! ISSPACE (c) && c != EOF)
743 BUF_ADD (c);
744 c = getc (current.file);
746 BUF_FINISH ();
748 ungetc (c, current.file);
750 nlmlex_file_push (lex_buf);
751 goto tail_recurse;
754 /* A non-space character at the start of a line must be the start of
755 a keyword. */
756 if (current.state == BEGINNING_OF_LINE)
758 BUF_INIT ();
759 while (ISALNUM (c) || c == '_')
761 BUF_ADD (TOUPPER (c));
762 c = getc (current.file);
764 BUF_FINISH ();
766 if (c != EOF && ! ISSPACE (c) && c != ',')
768 nlmheader_identify ();
769 fprintf (stderr, _("%s:%d: illegal character in keyword: %c\n"),
770 current.name, current.lineno, c);
772 else
774 unsigned int i;
776 for (i = 0; i < KEYWORD_COUNT; i++)
778 if (lex_buf[0] == keyword_tokens[i].keyword[0]
779 && strcmp (lex_buf, keyword_tokens[i].keyword) == 0)
781 /* Pushing back the final whitespace avoids worrying
782 about \n here. */
783 ungetc (c, current.file);
784 current.state = IN_LINE;
785 return keyword_tokens[i].token;
789 nlmheader_identify ();
790 fprintf (stderr, _("%s:%d: unrecognized keyword: %s\n"),
791 current.name, current.lineno, lex_buf);
794 ++parse_errors;
795 /* Treat the rest of this line as a comment. */
796 ungetc (COMMENT_CHAR, current.file);
797 goto tail_recurse;
800 /* Parentheses just represent themselves. */
801 if (c == '(' || c == ')')
802 return c;
804 /* Handle quoted strings. */
805 if (c == '"' || c == '\'')
807 int quote;
808 int start_lineno;
810 quote = c;
811 start_lineno = current.lineno;
813 c = getc (current.file);
814 BUF_INIT ();
815 while (c != quote && c != EOF)
817 BUF_ADD (c);
818 if (c == '\n')
819 ++current.lineno;
820 c = getc (current.file);
822 BUF_FINISH ();
824 if (c == EOF)
826 nlmheader_identify ();
827 fprintf (stderr, _("%s:%d: end of file in quoted string\n"),
828 current.name, start_lineno);
829 ++parse_errors;
832 /* FIXME: Possible memory leak. */
833 yylval.string = xstrdup (lex_buf);
834 return QUOTED_STRING;
837 /* Gather a generic argument. */
838 BUF_INIT ();
839 while (! ISSPACE (c)
840 && c != ','
841 && c != COMMENT_CHAR
842 && c != '('
843 && c != ')')
845 BUF_ADD (c);
846 c = getc (current.file);
848 BUF_FINISH ();
850 ungetc (c, current.file);
852 /* FIXME: Possible memory leak. */
853 yylval.string = xstrdup (lex_buf);
854 return STRING;
857 /* Get a number from a string. */
859 static long
860 nlmlex_get_number (s)
861 const char *s;
863 long ret;
864 char *send;
866 ret = strtol (s, &send, 10);
867 if (*send != '\0')
868 nlmheader_warn (_("bad number"), -1);
869 return ret;
872 /* Prefix the nlmconv warnings with a note as to where they come from.
873 We don't use program_name on every warning, because then some
874 versions of the emacs next-error function can't recognize the line
875 number. */
877 static void
878 nlmheader_identify ()
880 static int done;
882 if (! done)
884 fprintf (stderr, _("%s: problems in NLM command language input:\n"),
885 program_name);
886 done = 1;
890 /* Issue a warning. */
892 static void
893 nlmheader_warn (s, imax)
894 const char *s;
895 int imax;
897 nlmheader_identify ();
898 fprintf (stderr, "%s:%d: %s", current.name, current.lineno, s);
899 if (imax != -1)
900 fprintf (stderr, " (max %d)", imax);
901 fprintf (stderr, "\n");
904 /* Report an error. */
906 static void
907 nlmheader_error (s)
908 const char *s;
910 nlmheader_warn (s, -1);
911 ++parse_errors;
914 /* Add a string to a string list. */
916 static struct string_list *
917 string_list_cons (s, l)
918 char *s;
919 struct string_list *l;
921 struct string_list *ret;
923 ret = (struct string_list *) xmalloc (sizeof (struct string_list));
924 ret->next = l;
925 ret->string = s;
926 return ret;
929 /* Append a string list to another string list. */
931 static struct string_list *
932 string_list_append (l1, l2)
933 struct string_list *l1;
934 struct string_list *l2;
936 register struct string_list **pp;
938 for (pp = &l1; *pp != NULL; pp = &(*pp)->next)
940 *pp = l2;
941 return l1;
944 /* Append a string to a string list. */
946 static struct string_list *
947 string_list_append1 (l, s)
948 struct string_list *l;
949 char *s;
951 struct string_list *n;
952 register struct string_list **pp;
954 n = (struct string_list *) xmalloc (sizeof (struct string_list));
955 n->next = NULL;
956 n->string = s;
957 for (pp = &l; *pp != NULL; pp = &(*pp)->next)
959 *pp = n;
960 return l;
963 /* Duplicate a string in memory. */
965 static char *
966 xstrdup (s)
967 const char *s;
969 unsigned long len;
970 char *ret;
972 len = strlen (s);
973 ret = xmalloc (len + 1);
974 strcpy (ret, s);
975 return ret;