/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * read.c * * This file contains the makefile reader. */ /* * Included files */ #include /* alloca() */ #include /* errno */ #include /* fcntl() */ #include #include /* expand_value(), expand_macro() */ #include /* getmem() */ #include /* get_next_block_fn() */ #include /* read() */ #include /* read(), unlink() */ #include #include #include /* * typedefs & structs */ /* * Static variables */ static int line_started_with_space=0; // Used to diagnose spaces instead of tabs /* * File table of contents */ static void parse_makefile(register Name true_makefile_name, register Source source); static Source push_macro_value(register Source bp, register wchar_t *buffer, int size, register Source source); extern void enter_target_groups_and_dependencies(Name_vector target, Name_vector depes, Cmd_line command, Separator separator, Boolean target_group_seen); extern Name normalize_name(register wchar_t *name_string, register int length); /* * read_simple_file(makefile_name, chase_path, doname_it, * complain, must_exist, report_file, lock_makefile) * * Make the makefile and setup to read it. Actually read it if it is stdio * * Return value: * false if the read failed * * Parameters: * makefile_name Name of the file to read * chase_path Use the makefile path when opening file * doname_it Call doname() to build the file first * complain Print message if doname/open fails * must_exist Generate fatal if file is missing * report_file Report file when running -P * lock_makefile Lock the makefile when reading * * Static variables used: * * Global variables used: * do_not_exec_rule Is -n on? * file_being_read Set to the name of the new file * line_number The number of the current makefile line * makefiles_used A list of all makefiles used, appended to */ Boolean read_simple_file(register Name makefile_name, register Boolean chase_path, register Boolean doname_it, Boolean complain, Boolean must_exist, Boolean report_file, Boolean lock_makefile) { static short max_include_depth; register Property makefile = maybe_append_prop(makefile_name, makefile_prop); Boolean forget_after_parse = false; static pathpt makefile_path; register int n; char *path; register Source source = ALLOC(Source); Property orig_makefile = makefile; Dependency *dpp; Dependency dp; register int length; wchar_t *previous_file_being_read = file_being_read; int previous_line_number = line_number; wchar_t previous_current_makefile[MAXPATHLEN]; Makefile_type save_makefile_type; Name normalized_makefile_name; register wchar_t *string_start; register wchar_t *string_end; wchar_t * wcb = get_wstring(makefile_name->string_mb); if (max_include_depth++ >= 40) { fatal(gettext("Too many nested include statements")); } if (makefile->body.makefile.contents != NULL) { retmem(makefile->body.makefile.contents); } source->inp_buf = source->inp_buf_ptr = source->inp_buf_end = NULL; source->error_converting = false; makefile->body.makefile.contents = NULL; makefile->body.makefile.size = 0; if ((makefile_name->hash.length != 1) || (wcb[0] != (int) hyphen_char)) { if ((makefile->body.makefile.contents == NULL) && (doname_it)) { if (makefile_path == NULL) { char *pfx = make_install_prefix(); char *path; add_dir_to_path(".", &makefile_path, -1); // As regularly installed asprintf(&path, "%s/../share/somake", pfx); add_dir_to_path(path, &makefile_path, -1); free(path); // As regularly installed asprintf(&path, "%s/../share/lib/make", pfx); add_dir_to_path(path, &makefile_path, -1); free(path); // Tools build asprintf(&path, "%s/../../share/", pfx); add_dir_to_path(path, &makefile_path, -1); free(path); add_dir_to_path("/usr/share/lib/make", &makefile_path, -1); add_dir_to_path("/etc/default", &makefile_path, -1); free(pfx); } save_makefile_type = makefile_type; makefile_type = reading_nothing; if (doname(makefile_name, true, false) == build_dont_know) { /* Try normalized filename */ string_start=get_wstring(makefile_name->string_mb); for (string_end=string_start+1; *string_end != L'\0'; string_end++); normalized_makefile_name=normalize_name(string_start, string_end - string_start); if ((strcmp(makefile_name->string_mb, normalized_makefile_name->string_mb) == 0) || (doname(normalized_makefile_name, true, false) == build_dont_know)) { n = access_vroot(makefile_name->string_mb, 4, chase_path ? makefile_path : NULL, VROOT_DEFAULT); if (n == 0) { get_vroot_path((char **) NULL, &path, (char **) NULL); if ((path[0] == (int) period_char) && (path[1] == (int) slash_char)) { path += 2; } MBSTOWCS(wcs_buffer, path); makefile_name = GETNAME(wcs_buffer, FIND_LENGTH); } } retmem(string_start); /* * Commented out: retmem_mb(normalized_makefile_name->string_mb); * We have to return this memory, but it seems to trigger a bug * in dmake or in Sun C++ 5.7 compiler (it works ok if this code * is compiled using Sun C++ 5.6). */ // retmem_mb(normalized_makefile_name->string_mb); } makefile_type = save_makefile_type; } source->string.free_after_use = false; source->previous = NULL; source->already_expanded = false; /* Lock the file for read, but not when -n. */ if (lock_makefile && !do_not_exec_rule) { make_state_lockfile = getmem(strlen(make_state->string_mb) + strlen(".lock") + 1); (void) sprintf(make_state_lockfile, "%s.lock", make_state->string_mb); (void) file_lock(make_state->string_mb, make_state_lockfile, (int *) &make_state_locked, 0); if(!make_state_locked) { printf("-- NO LOCKING for read\n"); retmem_mb(make_state_lockfile); make_state_lockfile = 0; return failed; } } if (makefile->body.makefile.contents == NULL) { save_makefile_type = makefile_type; makefile_type = reading_nothing; if ((doname_it) && (doname(makefile_name, true, false) == build_failed)) { if (complain) { (void) fprintf(stderr, gettext("%s: Couldn't make `%s'\n"), getprogname(), makefile_name->string_mb); } max_include_depth--; makefile_type = save_makefile_type; return failed; } makefile_type = save_makefile_type; // // Before calling exists() make sure that we have the right timestamp // makefile_name->stat.time = file_no_time; if (exists(makefile_name) == file_doesnt_exist) { if (complain || (makefile_name->stat.stat_errno != ENOENT)) { if (must_exist) { fatal(gettext("Can't find `%s': %s"), makefile_name->string_mb, errmsg(makefile_name-> stat.stat_errno)); } else { warning(gettext("Can't find `%s': %s"), makefile_name->string_mb, errmsg(makefile_name-> stat.stat_errno)); } } max_include_depth--; if(make_state_locked && (make_state_lockfile != NULL)) { (void) unlink(make_state_lockfile); retmem_mb(make_state_lockfile); make_state_lockfile = NULL; make_state_locked = false; } retmem(wcb); retmem_mb((char *)source); return failed; } /* * These values are the size and bytes of * the MULTI-BYTE makefile. */ orig_makefile->body.makefile.size = makefile->body.makefile.size = source->bytes_left_in_file = makefile_name->stat.size; if (report_file) { for (dpp = &makefiles_used; *dpp != NULL; dpp = &(*dpp)->next); dp = ALLOC(Dependency); dp->next = NULL; dp->name = makefile_name; dp->automatic = false; dp->stale = false; dp->built = false; *dpp = dp; } source->fd = open_vroot(makefile_name->string_mb, O_RDONLY, 0, NULL, VROOT_DEFAULT); if (source->fd < 0) { if (complain || (errno != ENOENT)) { if (must_exist) { fatal(gettext("Can't open `%s': %s"), makefile_name->string_mb, errmsg(errno)); } else { warning(gettext("Can't open `%s': %s"), makefile_name->string_mb, errmsg(errno)); } } max_include_depth--; return failed; } (void) fcntl(source->fd, F_SETFD, 1); orig_makefile->body.makefile.contents = makefile->body.makefile.contents = source->string.text.p = source->string.buffer.start = ALLOC_WC((int) (makefile_name->stat.size + 2)); if (makefile_type == reading_cpp_file) { forget_after_parse = true; } source->string.text.end = source->string.text.p; source->string.buffer.end = source->string.text.p + makefile_name->stat.size; } else { /* Do we ever reach here? */ source->fd = -1; source->string.text.p = source->string.buffer.start = makefile->body.makefile.contents; source->string.text.end = source->string.buffer.end = source->string.text.p + makefile->body.makefile.size; source->bytes_left_in_file = makefile->body.makefile.size; } file_being_read = wcb; } else { char *stdin_text_p; char *stdin_text_end; char *stdin_buffer_start; char *stdin_buffer_end; char *p_mb; int num_mb_chars; size_t num_wc_chars; MBSTOWCS(wcs_buffer, "Standard in"); makefile_name = GETNAME(wcs_buffer, FIND_LENGTH); /* * Memory to read standard in, then convert it * to wide char strings. */ stdin_buffer_start = stdin_text_p = getmem(length = 1024); stdin_buffer_end = stdin_text_p + length; MBSTOWCS(wcs_buffer, "standard input"); file_being_read = (wchar_t *) wcsdup(wcs_buffer); line_number = 0; while ((n = read(fileno(stdin), stdin_text_p, length)) > 0) { length -= n; stdin_text_p += n; if (length == 0) { p_mb = getmem(length = 1024 + (stdin_buffer_end - stdin_buffer_start)); (void) strncpy(p_mb, stdin_buffer_start, (stdin_buffer_end - stdin_buffer_start)); retmem_mb(stdin_buffer_start); stdin_text_p = p_mb + (stdin_buffer_end - stdin_buffer_start); stdin_buffer_start = p_mb; stdin_buffer_end = stdin_buffer_start + length; length = 1024; } } if (n < 0) { fatal(gettext("Error reading standard input: %s"), errmsg(errno)); } stdin_text_p = stdin_buffer_start; stdin_text_end = stdin_buffer_end - length; num_mb_chars = stdin_text_end - stdin_text_p; /* * Now, convert the sequence of multibyte chars into * a sequence of corresponding wide character codes. */ source->string.free_after_use = false; source->previous = NULL; source->bytes_left_in_file = 0; source->fd = -1; source->already_expanded = false; source->string.buffer.start = source->string.text.p = ALLOC_WC(num_mb_chars + 1); source->string.buffer.end = source->string.text.p + num_mb_chars; num_wc_chars = mbstowcs(source->string.text.p, stdin_text_p, num_mb_chars); if ((int) num_wc_chars >= 0) { source->string.text.end = source->string.text.p + num_wc_chars; } (void) retmem_mb(stdin_text_p); } line_number = 1; if (trace_reader) { (void) printf(gettext(">>>>>>>>>>>>>>>> Reading makefile %s\n"), makefile_name->string_mb); } parse_makefile(makefile_name, source); if (trace_reader) { (void) printf(gettext(">>>>>>>>>>>>>>>> End of makefile %s\n"), makefile_name->string_mb); } if(file_being_read) { retmem(file_being_read); } file_being_read = previous_file_being_read; line_number = previous_line_number; makefile_type = reading_nothing; max_include_depth--; if (make_state_locked) { /* Unlock .make.state. */ unlink(make_state_lockfile); make_state_locked = false; retmem_mb(make_state_lockfile); } if (forget_after_parse) { retmem(makefile->body.makefile.contents); makefile->body.makefile.contents = NULL; } retmem_mb((char *)source); return succeeded; } /* * parse_makefile(true_makefile_name, source) * * Strings are read from Sources. * When macros are found, their values are represented by a * Source that is pushed on a stack. At end of string * (that is returned from GET_CHAR() as 0), the block is popped. * * Parameters: * true_makefile_name The name of makefile we are parsing * source The source block to read from * * Global variables used: * do_not_exec_rule Is -n on? * line_number The number of the current makefile line * makefile_type What kind of makefile are we reading? * empty_name The Name "" */ static void parse_makefile(register Name true_makefile_name, register Source source) { /* char mb_buffer[MB_LEN_MAX]; */ register wchar_t *source_p; register wchar_t *source_end; register wchar_t *string_start; wchar_t *string_end; register Boolean macro_seen_in_string; Boolean append; String_rec name_string; wchar_t name_buffer[STRING_BUFFER_LENGTH]; register int distance; register int paren_count; int brace_count; int char_number; Cmd_line command; Cmd_line command_tail; Name macro_value; Name_vector_rec target; Name_vector_rec depes; Name_vector_rec extra_name_vector; Name_vector current_names; Name_vector extra_names = &extra_name_vector; Name_vector nvp; Boolean target_group_seen; register Reader_state state; register Reader_state on_eoln_state; register Separator separator; wchar_t buffer[4 * STRING_BUFFER_LENGTH]; Source extrap; Boolean save_do_not_exec_rule = do_not_exec_rule; Name makefile_name; static Name sh_name; static Name shell_name; int i; static wchar_t include_space[10]; static wchar_t include_tab[10]; int tmp_bytes_left_in_string; Boolean tmp_maybe_include = false; int emptycount = 0; Boolean first_target; String_rec include_name; wchar_t include_buffer[STRING_BUFFER_LENGTH]; target.next = depes.next = NULL; /* Move some values from their struct to register declared locals */ CACHE_SOURCE(0); start_new_line: /* * Read whitespace on old line. Leave pointer on first char on * next line. */ first_target = true; on_eoln_state = exit_state; /* for (WCTOMB(mb_buffer, GET_CHAR()); 1; source_p++, WCTOMB(mb_buffer, GET_CHAR())) switch (mb_buffer[0]) { */ for (char_number=0; 1; source_p++,char_number++) switch (GET_CHAR()) { case nul_char: /* End of this string. Pop it and return to the previous one */ GET_NEXT_BLOCK(source); source_p--; if (source == NULL) { GOTO_STATE(on_eoln_state); } break; case newline_char: end_of_line: source_p++; if (source->fd >= 0) { line_number++; } switch (GET_CHAR()) { case nul_char: GET_NEXT_BLOCK(source); if (source == NULL) { GOTO_STATE(on_eoln_state); } /* Go back to the top of this loop */ goto start_new_line; case newline_char: case numbersign_char: case dollar_char: case space_char: case tab_char: /* * Go back to the top of this loop since the * new line does not start with a regular char. */ goto start_new_line; default: /* We found the first proper char on the new line */ goto start_new_line_no_skip; } case space_char: if (char_number == 0) line_started_with_space=line_number; case tab_char: /* Whitespace. Just keep going in this loop */ break; case numbersign_char: /* Comment. Skip over it */ for (; 1; source_p++) { switch (GET_CHAR()) { case nul_char: GET_NEXT_BLOCK_NOCHK(source); if (source == NULL) { GOTO_STATE(on_eoln_state); } if (source->error_converting) { // Illegal byte sequence - skip its first byte source->inp_buf_ptr++; } source_p--; break; case backslash_char: /* Comments can be continued */ if (*++source_p == (int) nul_char) { GET_NEXT_BLOCK_NOCHK(source); if (source == NULL) { GOTO_STATE(on_eoln_state); } if (source->error_converting) { // Illegal byte sequence - skip its first byte source->inp_buf_ptr++; source_p--; break; } } if(*source_p == (int) newline_char) { if (source->fd >= 0) { line_number++; } } break; case newline_char: /* * After we skip the comment we go to * the end of line handler since end of * line terminates comments. */ goto end_of_line; } } case dollar_char: /* Macro reference */ if (source->already_expanded) { /* * If we are reading from the expansion of a * macro we already expanded everything enough. */ goto start_new_line_no_skip; } /* * Expand the value and push the Source on the stack of * things being read. */ source_p++; UNCACHE_SOURCE(); { Source t = (Source) alloca((int) sizeof (Source_rec)); source = push_macro_value(t, buffer, sizeof buffer, source); } CACHE_SOURCE(1); break; default: /* We found the first proper char on the new line */ goto start_new_line_no_skip; } /* * We found the first normal char (one that starts an identifier) * on the newline. */ start_new_line_no_skip: /* Inspect that first char to see if it maybe is special anyway */ switch (GET_CHAR()) { case nul_char: GET_NEXT_BLOCK(source); if (source == NULL) { GOTO_STATE(on_eoln_state); } goto start_new_line_no_skip; case newline_char: /* Just in case */ goto start_new_line; case exclam_char: /* Evaluate the line before it is read */ string_start = source_p + 1; macro_seen_in_string = false; /* Stuff the line in a string so we can eval it. */ for (; 1; source_p++) { switch (GET_CHAR()) { case newline_char: goto eoln_1; case nul_char: if (source->fd > 0) { if (!macro_seen_in_string) { macro_seen_in_string = true; INIT_STRING_FROM_STACK( name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); GET_NEXT_BLOCK(source); string_start = source_p; source_p--; break; } eoln_1: if (!macro_seen_in_string) { INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); extrap = (Source) alloca((int) sizeof (Source_rec)); extrap->string.buffer.start = NULL; extrap->inp_buf = extrap->inp_buf_ptr = extrap->inp_buf_end = NULL; extrap->error_converting = false; if (*source_p == (int) nul_char) { source_p++; } /* Eval the macro */ expand_value(GETNAME(name_string.buffer.start, FIND_LENGTH), &extrap->string, false); if (name_string.free_after_use) { retmem(name_string.buffer.start); } UNCACHE_SOURCE(); extrap->string.text.p = extrap->string.buffer.start; extrap->fd = -1; /* And push the value */ extrap->previous = source; source = extrap; CACHE_SOURCE(0); goto line_evald; } } default: goto line_evald; } /* We now have a line we can start reading */ line_evald: if (source == NULL) { GOTO_STATE(exit_state); } /* Check if this is an include command */ if ((makefile_type == reading_makefile) && !source->already_expanded) { if (include_space[0] == (int) nul_char) { MBSTOWCS(include_space, "include "); MBSTOWCS(include_tab, "include\t"); } if ((IS_WEQUALN(source_p, include_space, 8)) || (IS_WEQUALN(source_p, include_tab, 8))) { source_p += 7; if (iswspace(*source_p)) { Makefile_type save_makefile_type; wchar_t *name_start; int name_length; /* * Yes, this is an include. * Skip spaces to get to the filename. */ while (iswspace(*source_p) || (*source_p == (int) nul_char)) { switch (GET_CHAR()) { case nul_char: GET_NEXT_BLOCK(source); if (source == NULL) { GOTO_STATE(on_eoln_state); } break; default: source_p++; break; } } string_start = source_p; /* Find the end of the filename */ macro_seen_in_string = false; while (!iswspace(*source_p) || (*source_p == (int) nul_char)) { switch (GET_CHAR()) { case nul_char: if (!macro_seen_in_string) { INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); macro_seen_in_string = true; GET_NEXT_BLOCK(source); string_start = source_p; if (source == NULL) { GOTO_STATE(on_eoln_state); } break; default: source_p++; break; } } source->string.text.p = source_p; if (macro_seen_in_string) { append_string(string_start, &name_string, source_p - string_start); name_start = name_string.buffer.start; name_length = name_string.text.p - name_start; } else { name_start = string_start; name_length = source_p - string_start; } /* Strip "./" from the head of the name */ if ((name_start[0] == (int) period_char) && (name_start[1] == (int) slash_char)) { name_start += 2; name_length -= 2; } /* if include file name is surrounded by double quotes */ if ((name_start[0] == (int) doublequote_char) && (name_start[name_length - 1] == (int) doublequote_char)) { name_start += 1; name_length -= 2; /* if name does not begin with a slash char */ if (name_start[0] != (int) slash_char) { if ((name_start[0] == (int) period_char) && (name_start[1] == (int) slash_char)) { name_start += 2; name_length -= 2; } INIT_STRING_FROM_STACK(include_name, include_buffer); APPEND_NAME(true_makefile_name, &include_name, true_makefile_name->hash.length); wchar_t *slash = wcsrchr(include_name.buffer.start, (int) slash_char); if (slash != NULL) { include_name.text.p = slash + 1; append_string(name_start, &include_name, name_length); name_start = include_name.buffer.start; name_length = include_name.text.p - name_start; } } } /* Even when we run -n we want to create makefiles */ do_not_exec_rule = false; makefile_name = GETNAME(name_start, name_length); if (makefile_name->dollar) { String_rec destination; wchar_t buffer[STRING_BUFFER_LENGTH]; wchar_t *p; wchar_t *q; INIT_STRING_FROM_STACK(destination, buffer); expand_value(makefile_name, &destination, false); for (p = destination.buffer.start; (*p != (int) nul_char) && iswspace(*p); p++); for (q = p; (*q != (int) nul_char) && !iswspace(*q); q++); makefile_name = GETNAME(p, q-p); if (destination.free_after_use) { retmem(destination.buffer.start); } } source_p++; UNCACHE_SOURCE(); /* Read the file */ save_makefile_type = makefile_type; if (read_simple_file(makefile_name, true, true, true, false, true, false) == failed) { fatal_reader(gettext("Read of include file `%s' failed"), makefile_name->string_mb); } makefile_type = save_makefile_type; do_not_exec_rule = save_do_not_exec_rule; CACHE_SOURCE(0); goto start_new_line; } else { source_p -= 7; } } else { /* Check if the word include was split across 8K boundary. */ tmp_bytes_left_in_string = source->string.text.end - source_p; if (tmp_bytes_left_in_string < 8) { tmp_maybe_include = false; if (IS_WEQUALN(source_p, include_space, tmp_bytes_left_in_string)) { tmp_maybe_include = true; } if (tmp_maybe_include) { GET_NEXT_BLOCK(source); tmp_maybe_include = false; goto line_evald; } } } } /* Reset the status in preparation for the new line */ for (nvp = ⌖ nvp != NULL; nvp = nvp->next) { nvp->used = 0; } for (nvp = &depes; nvp != NULL; nvp = nvp->next) { nvp->used = 0; } target_group_seen = false; command = command_tail = NULL; macro_value = NULL; append = false; current_names = ⌖ SET_STATE(scan_name_state); on_eoln_state = illegal_eoln_state; separator = none_seen; /* The state machine starts here */ enter_state: while (1) switch (state) { /**************************************************************** * Scan name state */ case scan_name_state: /* Scan an identifier. We skip over chars until we find a break char */ /* First skip white space. */ for (; 1; source_p++) switch (GET_CHAR()) { case nul_char: GET_NEXT_BLOCK(source); source_p--; if (source == NULL) { GOTO_STATE(on_eoln_state); } break; case newline_char: /* We found the end of the line. */ /* Do postprocessing or return error */ source_p++; if (source->fd >= 0) { line_number++; } GOTO_STATE(on_eoln_state); case backslash_char: /* Continuation */ if (*++source_p == (int) nul_char) { GET_NEXT_BLOCK(source); if (source == NULL) { GOTO_STATE(on_eoln_state); } } if (*source_p == (int) newline_char) { if (source->fd >= 0) { line_number++; } } else { source_p--; } break; case tab_char: case space_char: /* Whitespace is skipped */ break; case numbersign_char: /* Comment. Skip over it */ for (; 1; source_p++) { switch (GET_CHAR()) { case nul_char: GET_NEXT_BLOCK_NOCHK(source); if (source == NULL) { GOTO_STATE(on_eoln_state); } if (source->error_converting) { // Illegal byte sequence - skip its first byte source->inp_buf_ptr++; } source_p--; break; case backslash_char: if (*++source_p == (int) nul_char) { GET_NEXT_BLOCK_NOCHK(source); if (source == NULL) { GOTO_STATE(on_eoln_state); } if (source->error_converting) { // Illegal byte sequence - skip its first byte source->inp_buf_ptr++; source_p--; break; } } if(*source_p == (int) newline_char) { if (source->fd >= 0) { line_number++; } } break; case newline_char: source_p++; if (source->fd >= 0) { line_number++; } GOTO_STATE(on_eoln_state); } } case dollar_char: /* Macro reference. Expand and push value */ if (source->already_expanded) { goto scan_name; } source_p++; UNCACHE_SOURCE(); { Source t = (Source) alloca((int) sizeof (Source_rec)); source = push_macro_value(t, buffer, sizeof buffer, source); } CACHE_SOURCE(1); break; default: /* End of white space */ goto scan_name; } /* First proper identifier character */ scan_name: string_start = source_p; paren_count = brace_count = 0; macro_seen_in_string = false; resume_name_scan: for (; 1; source_p++) { switch (GET_CHAR()) { case nul_char: /* Save what we have seen so far of the identifier */ if (source_p != string_start) { if (!macro_seen_in_string) { INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); macro_seen_in_string = true; } /* Get more text to read */ GET_NEXT_BLOCK(source); string_start = source_p; source_p--; if (source == NULL) { GOTO_STATE(on_eoln_state); } break; case newline_char: if (paren_count > 0) { fatal_reader(gettext("Unmatched `(' on line")); } if (brace_count > 0) { fatal_reader(gettext("Unmatched `{' on line")); } source_p++; /* Enter name */ current_names = enter_name(&name_string, macro_seen_in_string, string_start, source_p - 1, current_names, &extra_names, &target_group_seen); first_target = false; if (extra_names == NULL) { extra_names = (Name_vector) alloca((int) sizeof (Name_vector_rec)); } /* Do postprocessing or return error */ if (source->fd >= 0) { line_number++; } GOTO_STATE(on_eoln_state); case backslash_char: /* Check if this is a quoting backslash */ if (!macro_seen_in_string) { INIT_STRING_FROM_STACK(name_string, name_buffer); macro_seen_in_string = true; } append_string(string_start, &name_string, source_p - string_start); if (*++source_p == (int) nul_char) { GET_NEXT_BLOCK(source); if (source == NULL) { GOTO_STATE(on_eoln_state); } } if (*source_p == (int) newline_char) { if (source->fd >= 0) { line_number++; } *source_p = (int) space_char; string_start = source_p; goto resume_name_scan; } else { string_start = source_p; break; } break; case numbersign_char: if (paren_count + brace_count > 0) { break; } fatal_reader(gettext("Unexpected comment seen")); case dollar_char: if (source->already_expanded) { break; } /* Save the identifier so far */ if (source_p != string_start) { if (!macro_seen_in_string) { INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); macro_seen_in_string = true; } /* Eval and push the macro */ source_p++; UNCACHE_SOURCE(); { Source t = (Source) alloca((int) sizeof (Source_rec)); source = push_macro_value(t, buffer, sizeof buffer, source); } CACHE_SOURCE(1); string_start = source_p + 1; break; case parenleft_char: paren_count++; break; case parenright_char: if (--paren_count < 0) { fatal_reader(gettext("Unmatched `)' on line")); } break; case braceleft_char: brace_count++; break; case braceright_char: if (--brace_count < 0) { fatal_reader(gettext("Unmatched `}' on line")); } break; case ampersand_char: case greater_char: case bar_char: if (paren_count + brace_count == 0) { source_p++; } /* Fall into */ case tab_char: case space_char: if (paren_count + brace_count > 0) { break; } current_names = enter_name(&name_string, macro_seen_in_string, string_start, source_p, current_names, &extra_names, &target_group_seen); first_target = false; if (extra_names == NULL) { extra_names = (Name_vector) alloca((int) sizeof (Name_vector_rec)); } goto enter_state; case colon_char: if (paren_count + brace_count > 0) { break; } if (separator == conditional_seen) { break; } /** POSIX **/ #if 0 if(posix) { emptycount = 0; } #endif /** END POSIX **/ /* End of the target list. We now start reading */ /* dependencies or a conditional assignment */ if (separator != none_seen) { fatal_reader(gettext("Extra `:', `::', or `:=' on dependency line")); } /* Enter the last target */ if ((string_start != source_p) || macro_seen_in_string) { current_names = enter_name(&name_string, macro_seen_in_string, string_start, source_p, current_names, &extra_names, &target_group_seen); first_target = false; if (extra_names == NULL) { extra_names = (Name_vector) alloca((int) sizeof (Name_vector_rec)); } } /* Check if it is ":" "::" or ":=" */ scan_colon_label: switch (*++source_p) { case nul_char: GET_NEXT_BLOCK(source); source_p--; if (source == NULL) { GOTO_STATE(enter_dependencies_state); } goto scan_colon_label; case equal_char: if(svr4) { fatal_reader(gettext("syntax error")); } separator = conditional_seen; source_p++; current_names = &depes; GOTO_STATE(scan_name_state); case colon_char: separator = two_colon; source_p++; break; default: separator = one_colon; } current_names = &depes; on_eoln_state = enter_dependencies_state; GOTO_STATE(scan_name_state); case semicolon_char: if (paren_count + brace_count > 0) { break; } /* End of reading names. Start reading the rule */ if ((separator != one_colon) && (separator != two_colon)) { fatal_reader(gettext("Unexpected command seen")); } /* Enter the last dependency */ if ((string_start != source_p) || macro_seen_in_string) { current_names = enter_name(&name_string, macro_seen_in_string, string_start, source_p, current_names, &extra_names, &target_group_seen); first_target = false; if (extra_names == NULL) { extra_names = (Name_vector) alloca((int) sizeof (Name_vector_rec)); } } source_p++; /* Make sure to enter a rule even if the is */ /* no text here */ command = command_tail = ALLOC(Cmd_line); command->next = NULL; command->command_line = empty_name; command->make_refd = false; command->ignore_command_dependency = false; command->assign = false; command->ignore_error = false; command->silent = false; GOTO_STATE(scan_command_state); case plus_char: /* ** following code drops the target separator plus char if it starts ** a line. */ if(first_target && !macro_seen_in_string && source_p == string_start) { for (; 1; source_p++) switch (GET_CHAR()) { case nul_char: if (source_p != string_start) { if (!macro_seen_in_string) { INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); macro_seen_in_string = true; } GET_NEXT_BLOCK(source); string_start = source_p; source_p--; if (source == NULL) { GOTO_STATE(on_eoln_state); } break; case plus_char: source_p++; while (*source_p == (int) nul_char) { if (source_p != string_start) { if (!macro_seen_in_string) { INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); macro_seen_in_string = true; } GET_NEXT_BLOCK(source); string_start = source_p; if (source == NULL) { GOTO_STATE(on_eoln_state); } } if (*source_p == (int) tab_char || *source_p == (int) space_char) { macro_seen_in_string = false; string_start = source_p + 1; } else { goto resume_name_scan; } break; case tab_char: case space_char: string_start = source_p + 1; break; default: goto resume_name_scan; } } if (paren_count + brace_count > 0) { break; } /* We found "+=" construct */ if (source_p != string_start) { /* "+" is not a break char. */ /* Ignore it if it is part of an identifier */ source_p++; goto resume_name_scan; } /* Make sure the "+" is followed by a "=" */ scan_append: switch (*++source_p) { case nul_char: if (!macro_seen_in_string) { INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); GET_NEXT_BLOCK(source); source_p--; string_start = source_p; if (source == NULL) { GOTO_STATE(illegal_eoln_state); } goto scan_append; case equal_char: if(!svr4) { append = true; } else { fatal_reader(gettext("Must be a separator on rules")); } break; default: /* The "+" just starts a regular name. */ /* Start reading that name */ goto resume_name_scan; } /* Fall into */ case equal_char: if (paren_count + brace_count > 0) { break; } /* We found macro assignment. */ /* Check if it is legal and if it is appending */ switch (separator) { case none_seen: separator = equal_seen; on_eoln_state = enter_equal_state; break; case conditional_seen: on_eoln_state = enter_conditional_state; break; default: /* Reader must special check for "MACRO:sh=" */ /* notation */ if (sh_name == NULL) { MBSTOWCS(wcs_buffer, "sh"); sh_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, "shell"); shell_name = GETNAME(wcs_buffer, FIND_LENGTH); } if (!macro_seen_in_string) { INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start ); if ( (((target.used == 1) && (depes.used == 1) && (depes.names[0] == sh_name)) || ((target.used == 1) && (depes.used == 0) && (separator == one_colon) && (GETNAME(name_string.buffer.start,FIND_LENGTH) == sh_name))) && (!svr4)) { String_rec macro_name; wchar_t buffer[100]; INIT_STRING_FROM_STACK(macro_name, buffer); APPEND_NAME(target.names[0], ¯o_name, FIND_LENGTH); append_char((int) colon_char, ¯o_name); APPEND_NAME(sh_name, ¯o_name, FIND_LENGTH); target.names[0] = GETNAME(macro_name.buffer.start, FIND_LENGTH); separator = equal_seen; on_eoln_state = enter_equal_state; break; } else if ( (((target.used == 1) && (depes.used == 1) && (depes.names[0] == shell_name)) || ((target.used == 1) && (depes.used == 0) && (separator == one_colon) && (GETNAME(name_string.buffer.start,FIND_LENGTH) == shell_name))) && (!svr4)) { String_rec macro_name; wchar_t buffer[100]; INIT_STRING_FROM_STACK(macro_name, buffer); APPEND_NAME(target.names[0], ¯o_name, FIND_LENGTH); append_char((int) colon_char, ¯o_name); APPEND_NAME(shell_name, ¯o_name, FIND_LENGTH); target.names[0] = GETNAME(macro_name.buffer.start, FIND_LENGTH); separator = equal_seen; on_eoln_state = enter_equal_state; break; } if(svr4) { fatal_reader(gettext("syntax error")); } else { fatal_reader(gettext("Macro assignment on dependency line")); } } if (append) { source_p--; } /* Enter the macro name */ if ((string_start != source_p) || macro_seen_in_string) { current_names = enter_name(&name_string, macro_seen_in_string, string_start, source_p, current_names, &extra_names, &target_group_seen); first_target = false; if (extra_names == NULL) { extra_names = (Name_vector) alloca((int) sizeof (Name_vector_rec)); } } if (append) { source_p++; } macro_value = NULL; source_p++; distance = 0; /* Skip whitespace to the start of the value */ macro_seen_in_string = false; for (; 1; source_p++) { switch (GET_CHAR()) { case nul_char: GET_NEXT_BLOCK(source); source_p--; if (source == NULL) { GOTO_STATE(on_eoln_state); } break; case backslash_char: if (*++source_p == (int) nul_char) { GET_NEXT_BLOCK(source); if (source == NULL) { GOTO_STATE(on_eoln_state); } } if (*source_p != (int) newline_char) { if (!macro_seen_in_string) { macro_seen_in_string = true; INIT_STRING_FROM_STACK(name_string, name_buffer); } append_char((int) backslash_char, &name_string); append_char(*source_p, &name_string); string_start = source_p+1; goto macro_value_start; } else { if (source->fd >= 0) { line_number++; } } break; case newline_char: case numbersign_char: string_start = source_p; goto macro_value_end; case tab_char: case space_char: break; default: string_start = source_p; goto macro_value_start; } } macro_value_start: /* Find the end of the value */ for (; 1; source_p++) { if (distance != 0) { *source_p = *(source_p + distance); } switch (GET_CHAR()) { case nul_char: if (!macro_seen_in_string) { macro_seen_in_string = true; INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); GET_NEXT_BLOCK(source); string_start = source_p; source_p--; if (source == NULL) { GOTO_STATE(on_eoln_state); } break; case backslash_char: source_p++; if (distance != 0) { *source_p = *(source_p + distance); } if (*source_p == (int) nul_char) { if (!macro_seen_in_string) { macro_seen_in_string = true; INIT_STRING_FROM_STACK(name_string, name_buffer); } /* BID_1225561 */ *(source_p - 1) = (int) space_char; append_string(string_start, &name_string, source_p - string_start - 1); GET_NEXT_BLOCK(source); string_start = source_p; if (source == NULL) { GOTO_STATE(on_eoln_state); } if (distance != 0) { *source_p = *(source_p + distance); } if (*source_p == (int) newline_char) { append_char((int) space_char, &name_string); } else { append_char((int) backslash_char, &name_string); } /****************/ } if (*source_p == (int) newline_char) { source_p--; line_number++; distance++; *source_p = (int) space_char; while ((*(source_p + distance + 1) == (int) tab_char) || (*(source_p + distance + 1) == (int) space_char)) { distance++; } } break; case newline_char: case numbersign_char: goto macro_value_end; } } macro_value_end: /* Complete the value in the string */ if (!macro_seen_in_string) { macro_seen_in_string = true; INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); if (name_string.buffer.start != name_string.text.p) { macro_value = GETNAME(name_string.buffer.start, FIND_LENGTH); } if (name_string.free_after_use) { retmem(name_string.buffer.start); } for (; distance > 0; distance--) { *source_p++ = (int) space_char; } GOTO_STATE(on_eoln_state); } } /**************************************************************** * enter dependencies state */ case enter_dependencies_state: enter_dependencies_label: /* Expects pointer on first non whitespace char after last dependency. (On */ /* next line.) We end up here after having read a "targets : dependencies" */ /* line. The state checks if there is a rule to read and if so dispatches */ /* to scan_command_state scan_command_state reads one rule line and the */ /* returns here */ /* First check if the first char on the next line is special */ switch (GET_CHAR()) { case nul_char: GET_NEXT_BLOCK(source); if (source == NULL) { break; } goto enter_dependencies_label; case exclam_char: /* The line should be evaluate before it is read */ macro_seen_in_string = false; string_start = source_p + 1; for (; 1; source_p++) { switch (GET_CHAR()) { case newline_char: goto eoln_2; case nul_char: if (source->fd > 0) { if (!macro_seen_in_string) { macro_seen_in_string = true; INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); GET_NEXT_BLOCK(source); string_start = source_p; source_p--; break; } eoln_2: if (!macro_seen_in_string) { INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); extrap = (Source) alloca((int) sizeof (Source_rec)); extrap->string.buffer.start = NULL; extrap->inp_buf = extrap->inp_buf_ptr = extrap->inp_buf_end = NULL; extrap->error_converting = false; expand_value(GETNAME(name_string.buffer.start, FIND_LENGTH), &extrap->string, false); if (name_string.free_after_use) { retmem(name_string.buffer.start); } UNCACHE_SOURCE(); extrap->string.text.p = extrap->string.buffer.start; extrap->fd = -1; extrap->previous = source; source = extrap; CACHE_SOURCE(0); goto enter_dependencies_label; } } case dollar_char: if (source->already_expanded) { break; } source_p++; UNCACHE_SOURCE(); { Source t = (Source) alloca((int) sizeof (Source_rec)); source = push_macro_value(t, buffer, sizeof buffer, source); } CACHE_SOURCE(0); goto enter_dependencies_label; case numbersign_char: if (makefile_type != reading_makefile) { source_p++; GOTO_STATE(scan_command_state); } for (; 1; source_p++) { switch (GET_CHAR()) { case nul_char: GET_NEXT_BLOCK_NOCHK(source); if (source == NULL) { GOTO_STATE(on_eoln_state); } if (source->error_converting) { // Illegal byte sequence - skip its first byte source->inp_buf_ptr++; } source_p--; break; case backslash_char: if (*++source_p == (int) nul_char) { GET_NEXT_BLOCK_NOCHK(source); if (source == NULL) { GOTO_STATE(on_eoln_state); } if (source->error_converting) { // Illegal byte sequence - skip its first byte source->inp_buf_ptr++; source_p--; break; } } if(*source_p == (int) newline_char) { if (source->fd >= 0) { line_number++; } } break; case newline_char: source_p++; if (source->fd >= 0) { line_number++; } goto enter_dependencies_label; } } case tab_char: GOTO_STATE(scan_command_state); } /* We read all the command lines for the target/dependency line. */ /* Enter the stuff */ enter_target_groups_and_dependencies( &target, &depes, command, separator, target_group_seen); goto start_new_line; /**************************************************************** * scan command state */ case scan_command_state: /* We need to read one rule line. Do that and return to */ /* the enter dependencies state */ string_start = source_p; macro_seen_in_string = false; for (; 1; source_p++) { switch (GET_CHAR()) { case backslash_char: if (!macro_seen_in_string) { INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); macro_seen_in_string = true; if (*++source_p == (int) nul_char) { GET_NEXT_BLOCK(source); if (source == NULL) { string_start = source_p; goto command_newline; } } append_char((int) backslash_char, &name_string); append_char(*source_p, &name_string); if (*source_p == (int) newline_char) { if (source->fd >= 0) { line_number++; } if (*++source_p == (int) nul_char) { GET_NEXT_BLOCK(source); if (source == NULL) { string_start = source_p; goto command_newline; } } if (*source_p == (int) tab_char) { source_p++; } } else { if (*++source_p == (int) nul_char) { GET_NEXT_BLOCK(source); if (source == NULL) { string_start = source_p; goto command_newline; } } } string_start = source_p; if ((*source_p == (int) newline_char) || (*source_p == (int) backslash_char) || (*source_p == (int) nul_char)) { source_p--; } break; case newline_char: command_newline: if ((string_start != source_p) || macro_seen_in_string) { if (macro_seen_in_string) { append_string(string_start, &name_string, source_p - string_start); string_start = name_string.buffer.start; string_end = name_string.text.p; } else { string_end = source_p; } while ((*string_start != (int) newline_char) && iswspace(*string_start)){ string_start++; } if ((string_end > string_start) || (makefile_type == reading_statefile)) { if (command_tail == NULL) { command = command_tail = ALLOC(Cmd_line); } else { command_tail->next = ALLOC(Cmd_line); command_tail = command_tail->next; } command_tail->next = NULL; command_tail->make_refd = false; command_tail->ignore_command_dependency = false; command_tail->assign = false; command_tail->ignore_error = false; command_tail->silent = false; command_tail->command_line = GETNAME(string_start, string_end - string_start); if (macro_seen_in_string && name_string.free_after_use) { retmem(name_string. buffer.start); } } } do { if ((source != NULL) && (source->fd >= 0)) { line_number++; } if ((source != NULL) && (*++source_p == (int) nul_char)) { GET_NEXT_BLOCK(source); if (source == NULL) { GOTO_STATE(on_eoln_state); } } } while (*source_p == (int) newline_char); GOTO_STATE(enter_dependencies_state); case nul_char: if (!macro_seen_in_string) { INIT_STRING_FROM_STACK(name_string, name_buffer); } append_string(string_start, &name_string, source_p - string_start); macro_seen_in_string = true; GET_NEXT_BLOCK(source); string_start = source_p; source_p--; if (source == NULL) { GOTO_STATE(enter_dependencies_state); } break; } } /**************************************************************** * enter equal state */ case enter_equal_state: if (target.used != 1) { GOTO_STATE(poorly_formed_macro_state); } enter_equal(target.names[0], macro_value, append); goto start_new_line; /**************************************************************** * enter conditional state */ case enter_conditional_state: if (depes.used != 1) { GOTO_STATE(poorly_formed_macro_state); } for (nvp = ⌖ nvp != NULL; nvp = nvp->next) { for (i = 0; i < nvp->used; i++) { enter_conditional(nvp->names[i], depes.names[0], macro_value, append); } } goto start_new_line; /**************************************************************** * Error states */ case illegal_bytes_state: fatal_reader(gettext("Invalid byte sequence")); case illegal_eoln_state: if (line_number > 1) { if (line_started_with_space == (line_number - 1)) { line_number--; fatal_reader(gettext("Unexpected end of line seen\n\t*** missing separator (did you mean TAB instead of 8 spaces?)")); } } fatal_reader(gettext("Unexpected end of line seen")); case poorly_formed_macro_state: fatal_reader(gettext("Badly formed macro assignment")); case exit_state: return; default: fatal_reader(gettext("Internal error. Unknown reader state")); } } /* * push_macro_value(bp, buffer, size, source) * * Macro and function that evaluates one macro * and makes the reader read from the value of it * * Return value: * The source block to read the macro from * * Parameters: * bp The new source block to fill in * buffer Buffer to read from * size size of the buffer * source The old source block * * Global variables used: */ static Source push_macro_value(register Source bp, register wchar_t *buffer, int size, register Source source) { bp->string.buffer.start = bp->string.text.p = buffer; bp->string.text.end = NULL; bp->string.buffer.end = buffer + (size/SIZEOFWCHAR_T); bp->string.free_after_use = false; bp->inp_buf = bp->inp_buf_ptr = bp->inp_buf_end = NULL; bp->error_converting = false; expand_macro(source, &bp->string, (wchar_t *) NULL, false); bp->string.text.p = bp->string.buffer.start; /* 4209588: 'make' doesn't understand a macro with whitespaces in the head as target. * strip whitespace from the begining of the macro value */ while (iswspace(*bp->string.text.p)) { bp->string.text.p++; } bp->fd = -1; bp->already_expanded = true; bp->previous = source; return bp; } /* * enter_target_groups_and_dependencies(target, depes, command, separator, * target_group_seen) * * Parameters: * target Structure that shows the target(s) on the line * we are currently parsing. This can looks like * target1 .. targetN : dependencies * commands * or * target1 + .. + targetN : dependencies * commands * depes Dependencies * command Points to the command(s) to be executed for * this target. * separator : or :: or := * target_group_seen Set if we have target1 + .. + targetN * * * After reading the command lines for a target, this routine * is called to setup the dependencies and the commands for it. * If the target is a % pattern or part of a target group, then * the appropriate routines are called. */ void enter_target_groups_and_dependencies(Name_vector target, Name_vector depes, Cmd_line command, Separator separator, Boolean target_group_seen) { int i; Boolean reset= true; Chain target_group_member; Percent percent_ptr; for (; target != NULL; target = target->next) { for (i = 0; i < target->used; i++) { if (target->names[i] != NULL) { if (target_group_seen) { target_group_member = find_target_groups(target, i, reset); if(target_group_member == NULL) { fatal_reader(gettext("Unexpected '+' on dependency line")); } } reset = false; /* If we saw it in the makefile it must be * a file */ target->names[i]->stat.is_file = true; /* Make sure that we use dependencies * entered for makefiles */ target->names[i]->state = build_dont_know; /* If the target is special we delegate * the processing */ if (target->names[i]->special_reader != no_special) { special_reader(target->names[i], depes, command); } /* Check if this is a "a%b : x%y" type rule */ else if (target->names[i]->percent) { percent_ptr = enter_percent(target->names[i], target->target_group[i], depes, command); if (target_group_seen) { target_group_member->percent_member = percent_ptr; } } else if (target->names[i]->dollar) { enter_dyntarget(target->names[i]); enter_dependencies (target->names[i], target->target_group[i], depes, command, separator); } else { if (target_group_seen) { target_group_member->percent_member = NULL; } enter_dependencies (target->names[i], target->target_group[i], depes, command, separator); } } } } }