# # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2015, Richard Lowe. # Copyright 2019 RackTop Systems. LIBRARY = libmksh.a VERS = .1 OBJECTS = dosys.o \ globals.o \ i18n.o \ macro.o \ misc.o \ mksh.o \ read.o include $(SRC)/lib/Makefile.lib include ../../Makefile.com POFILE = libmksh.po MSGFILES = $(OBJECTS:%.o=%.cc) LIBS = $(LIBRARY) SRCDIR = ../ MAPFILES= CPPFLAGS += -D_FILE_OFFSET_BITS=64 CCERRWARN += -Wno-unused-variable CCERRWARN += -Wno-unused-function CCERRWARN += -Wno-unused-value CCERRWARN += -Wno-uninitialized all: $(LIBS) install: all $(POFILE): pofile_MSGFILES _msg: $(MSGDOMAINPOFILE) include $(SRC)/lib/Makefile.targ include $(SRC)/Makefile.msg.targ /* * 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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2015, Joyent, Inc. * Copyright 2019 RackTop Systems. */ /* * dosys.cc * * Execute one commandline */ /* * Included files */ #include /* WIFEXITED(status) */ #include /* alloca() */ #include /* errno */ #include /* errno */ #include /* open() */ #include #include /* getvar() */ #include /* getmem(), fatal_mksh(), errmsg() */ #include /* SIG_DFL */ #include /* open() */ #include /* wait() */ #include /* ulimit() */ #include /* close(), dup2() */ #include /* closefrom() */ #include /* * typedefs & structs */ /* * Static variables */ /* * File table of contents */ static Boolean exec_vp(char *name, char **argv, char **envp, Boolean ignore_error, pathpt vroot_path); /* * Workaround for NFS bug. Sometimes, when running 'open' on a remote * dmake server, it fails with "Stale NFS file handle" error. * The second attempt seems to work. */ int my_open(const char *path, int oflag, mode_t mode) { int res = open(path, oflag, mode); if (res < 0 && (errno == ESTALE || errno == EAGAIN)) { /* Stale NFS file handle. Try again */ res = open(path, oflag, mode); } return res; } /* * void * redirect_io(char *stdout_file, char *stderr_file) * * Redirects stdout and stderr for a child mksh process. */ void redirect_io(char *stdout_file, char *stderr_file) { int i; (void) closefrom(3); if ((i = my_open(stdout_file, O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC, S_IREAD | S_IWRITE)) < 0) { fatal_mksh(gettext("Couldn't open standard out temp file `%s': %s"), stdout_file, errmsg(errno)); } else { if (dup2(i, 1) == -1) { fatal_mksh("*** Error: dup2(3, 1) failed: %s", errmsg(errno)); } close(i); } if (stderr_file == NULL) { if (dup2(1, 2) == -1) { fatal_mksh("*** Error: dup2(1, 2) failed: %s", errmsg(errno)); } } else if ((i = my_open(stderr_file, O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC, S_IREAD | S_IWRITE)) < 0) { fatal_mksh(gettext("Couldn't open standard error temp file `%s': %s"), stderr_file, errmsg(errno)); } else { if (dup2(i, 2) == -1) { fatal_mksh("*** Error: dup2(3, 2) failed: %s", errmsg(errno)); } close(i); } } /* * doshell(command, ignore_error) * * Used to run command lines that include shell meta-characters. * The make macro SHELL is supposed to contain a path to the shell. * * Return value: * The pid of the process we started * * Parameters: * command The command to run * ignore_error Should we abort on error? * * Global variables used: * filter_stderr If -X is on we redirect stderr * shell_name The Name "SHELL", used to get the path to shell */ int doshell(wchar_t *command, Boolean ignore_error, char *stdout_file, char *stderr_file, int nice_prio) { char *argv[6]; int argv_index = 0; int cmd_argv_index; int length; char nice_prio_buf[MAXPATHLEN]; Name shell = getvar(shell_name); char *shellname; char *tmp_mbs_buffer; if (IS_EQUAL(shell->string_mb, "")) { shell = shell_name; } if ((shellname = strrchr(shell->string_mb, (int) slash_char)) == NULL) { shellname = shell->string_mb; } else { shellname++; } /* * Only prepend the /usr/bin/nice command to the original command * if the nice priority, nice_prio, is NOT zero (0). * Nice priorities can be a positive or a negative number. */ if (nice_prio != 0) { argv[argv_index++] = (char *)"nice"; (void) sprintf(nice_prio_buf, "-%d", nice_prio); argv[argv_index++] = strdup(nice_prio_buf); } argv[argv_index++] = shellname; argv[argv_index++] = (char*)(ignore_error ? "-c" : "-ce"); if ((length = wcslen(command)) >= MAXPATHLEN) { tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); (void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1); cmd_argv_index = argv_index; argv[argv_index++] = strdup(tmp_mbs_buffer); retmem_mb(tmp_mbs_buffer); } else { WCSTOMBS(mbs_buffer, command); cmd_argv_index = argv_index; argv[argv_index++] = strdup(mbs_buffer); } argv[argv_index] = NULL; (void) fflush(stdout); if ((childPid = fork()) == 0) { enable_interrupt((void (*) (int)) SIG_DFL); #if 0 if (filter_stderr) { redirect_stderr(); } #endif if (nice_prio != 0) { (void) execve("/usr/bin/nice", argv, environ); fatal_mksh(gettext("Could not load `/usr/bin/nice': %s"), errmsg(errno)); } else { (void) execve(shell->string_mb, argv, environ); fatal_mksh(gettext("Could not load Shell from `%s': %s"), shell->string_mb, errmsg(errno)); } } if (childPid == -1) { fatal_mksh(gettext("fork failed: %s"), errmsg(errno)); } retmem_mb(argv[cmd_argv_index]); return childPid; } /* * exec_vp(name, argv, envp, ignore_error) * * Like execve, but does path search. * This starts command when make invokes it directly (without a shell). * * Return value: * Returns false if the exec failed * * Parameters: * name The name of the command to run * argv Arguments for the command * envp The environment for it * ignore_error Should we abort on error? * * Global variables used: * shell_name The Name "SHELL", used to get the path to shell * vroot_path The path used by the vroot package */ static Boolean exec_vp(char *name, char **argv, char **envp, Boolean ignore_error, pathpt vroot_path) { Name shell = getvar(shell_name); char *shellname; char *shargv[4]; Name tmp_shell; if (IS_EQUAL(shell->string_mb, "")) { shell = shell_name; } for (int i = 0; i < 5; i++) { (void) execve_vroot(name, argv + 1, envp, vroot_path, VROOT_DEFAULT); switch (errno) { case ENOEXEC: case ENOENT: /* That failed. Let the shell handle it */ shellname = strrchr(shell->string_mb, (int) slash_char); if (shellname == NULL) { shellname = shell->string_mb; } else { shellname++; } shargv[0] = shellname; shargv[1] = (char*)(ignore_error ? "-c" : "-ce"); shargv[2] = argv[0]; shargv[3] = NULL; tmp_shell = getvar(shell_name); if (IS_EQUAL(tmp_shell->string_mb, "")) { tmp_shell = shell_name; } (void) execve_vroot(tmp_shell->string_mb, shargv, envp, vroot_path, VROOT_DEFAULT); return failed; case ETXTBSY: /* * The program is busy (debugged?). * Wait and then try again. */ (void) sleep((unsigned) i); case EAGAIN: break; default: return failed; } } return failed; } /* * doexec(command, ignore_error) * * Will scan an argument string and split it into words * thus building an argument list that can be passed to exec_ve() * * Return value: * The pid of the process started here * * Parameters: * command The command to run * ignore_error Should we abort on error? * * Global variables used: * filter_stderr If -X is on we redirect stderr */ int doexec(wchar_t *command, Boolean ignore_error, char *stdout_file, char *stderr_file, pathpt vroot_path, int nice_prio) { int arg_count = 5; char **argv; int length; char nice_prio_buf[MAXPATHLEN]; char **p; wchar_t *q; wchar_t *t; char *tmp_mbs_buffer; /* * Only prepend the /usr/bin/nice command to the original command * if the nice priority, nice_prio, is NOT zero (0). * Nice priorities can be a positive or a negative number. */ if (nice_prio != 0) { arg_count += 2; } for (t = command; *t != (int) nul_char; t++) { if (iswspace(*t)) { arg_count++; } } argv = (char **)alloca(arg_count * (sizeof(char *))); /* * Reserve argv[0] for sh in case of exec_vp failure. * Don't worry about prepending /usr/bin/nice command to argv[0]. * In fact, doing it may cause the sh command to fail! */ p = &argv[1]; if ((length = wcslen(command)) >= MAXPATHLEN) { tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); (void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1); argv[0] = strdup(tmp_mbs_buffer); retmem_mb(tmp_mbs_buffer); } else { WCSTOMBS(mbs_buffer, command); argv[0] = strdup(mbs_buffer); } if (nice_prio != 0) { *p++ = strdup("/usr/bin/nice"); (void) sprintf(nice_prio_buf, "-%d", nice_prio); *p++ = strdup(nice_prio_buf); } /* Build list of argument words. */ for (t = command; *t;) { if (p >= &argv[arg_count]) { /* This should never happen, right? */ WCSTOMBS(mbs_buffer, command); fatal_mksh(gettext("Command `%s' has more than %d arguments"), mbs_buffer, arg_count); } q = t; while (!iswspace(*t) && (*t != (int) nul_char)) { t++; } if (*t) { for (*t++ = (int) nul_char; iswspace(*t); t++); } if ((length = wcslen(q)) >= MAXPATHLEN) { tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); (void) wcstombs(tmp_mbs_buffer, q, (length * MB_LEN_MAX) + 1); *p++ = strdup(tmp_mbs_buffer); retmem_mb(tmp_mbs_buffer); } else { WCSTOMBS(mbs_buffer, q); *p++ = strdup(mbs_buffer); } } *p = NULL; /* Then exec the command with that argument list. */ (void) fflush(stdout); if ((childPid = fork()) == 0) { enable_interrupt((void (*) (int)) SIG_DFL); #if 0 if (filter_stderr) { redirect_stderr(); } #endif (void) exec_vp(argv[1], argv, environ, ignore_error, vroot_path); fatal_mksh(gettext("Cannot load command `%s': %s"), argv[1], errmsg(errno)); } if (childPid == -1) { fatal_mksh(gettext("fork failed: %s"), errmsg(errno)); } for (int i = 0; argv[i] != NULL; i++) { retmem_mb(argv[i]); } return childPid; } /* * await(ignore_error, silent_error, target, command, running_pid) * * Wait for one child process and analyzes * the returned status when the child process terminates. * * Return value: * Returns true if commands ran OK * * Parameters: * ignore_error Should we abort on error? * silent_error Should error messages be suppressed for dmake? * target The target we are building, for error msgs * command The command we ran, for error msgs * running_pid The pid of the process we are waiting for * * Static variables used: * filter_file The fd for the filter file * filter_file_name The name of the filter file * * Global variables used: * filter_stderr Set if -X is on */ Boolean await(Boolean ignore_error, Boolean silent_error, Name target, wchar_t *command, pid_t running_pid, void *xdrs_p, int job_msg_id) { int status; char *buffer; int core_dumped; int exit_status; FILE *outfp; pid_t pid; struct stat stat_buff; int termination_signal; while ((pid = wait(&status)) != running_pid) { if (pid == -1) { fatal_mksh(gettext("wait() failed: %s"), errmsg(errno)); } } (void) fflush(stdout); (void) fflush(stderr); if (status == 0) { #ifdef PRINT_EXIT_STATUS warning_mksh("I'm in await(), and status is 0."); #endif return succeeded; } #ifdef PRINT_EXIT_STATUS warning_mksh("I'm in await(), and status is *NOT* 0."); #endif exit_status = WEXITSTATUS(status); #ifdef PRINT_EXIT_STATUS warning_mksh("I'm in await(), and exit_status is %d.", exit_status); #endif termination_signal = WTERMSIG(status); core_dumped = WCOREDUMP(status); /* * If the child returned an error, we now try to print a * nice message about it. */ if (!silent_error) { if (exit_status != 0) { (void) fprintf(stdout, gettext("*** Error code %d"), exit_status); } else { (void) fprintf(stdout, gettext("*** Signal %d"), termination_signal); if (core_dumped) { (void) fprintf(stdout, gettext(" - core dumped")); } } if (ignore_error) { (void) fprintf(stdout, gettext(" (ignored)")); } (void) fprintf(stdout, "\n"); (void) fflush(stdout); } #ifdef PRINT_EXIT_STATUS warning_mksh("I'm in await(), returning failed."); #endif return failed; } /* * sh_command2string(command, destination) * * Run one sh command and capture the output from it. * * Return value: * * Parameters: * command The command to run * destination Where to deposit the output from the command * * Static variables used: * * Global variables used: */ void sh_command2string(String command, String destination) { FILE *fd; int chr; int status; Boolean command_generated_output = false; command->text.p = NULL; WCSTOMBS(mbs_buffer, command->buffer.start); if ((fd = popen(mbs_buffer, "r")) == NULL) { WCSTOMBS(mbs_buffer, command->buffer.start); fatal_mksh(gettext("Could not run command `%s' for :sh transformation"), mbs_buffer); } while ((chr = getc(fd)) != EOF) { if (chr == (int) newline_char) { chr = (int) space_char; } command_generated_output = true; append_char(chr, destination); } /* * We don't want to keep the last LINE_FEED since usually * the output of the 'sh:' command is used to evaluate * some MACRO. ( /bin/sh and other shell add a line feed * to the output so that the prompt appear in the right place. * We don't need that */ if (command_generated_output){ if ( *(destination->text.p-1) == (int) space_char) { * (-- destination->text.p) = '\0'; } } else { /* * If the command didn't generate any output, * set the buffer to a null string. */ *(destination->text.p) = '\0'; } status = pclose(fd); if (status != 0) { WCSTOMBS(mbs_buffer, command->buffer.start); fatal_mksh(gettext("The command `%s' returned status `%d'"), mbs_buffer, WEXITSTATUS(status)); } } /* * 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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * globals.cc * * This declares all global variables */ /* * Included files */ #include /* * Defined macros */ /* * typedefs & structs */ /* * Global variables */ char char_semantics[CHAR_SEMANTICS_ENTRIES]; wchar_t char_semantics_char[] = { ampersand_char, asterisk_char, at_char, backquote_char, backslash_char, bar_char, bracketleft_char, bracketright_char, colon_char, dollar_char, doublequote_char, equal_char, exclam_char, greater_char, hat_char, hyphen_char, less_char, newline_char, numbersign_char, parenleft_char, parenright_char, percent_char, plus_char, question_char, quote_char, semicolon_char, nul_char }; Macro_list cond_macro_list; Boolean conditional_macro_used; Boolean do_not_exec_rule; /* `-n' */ Boolean dollarget_seen; Boolean dollarless_flag; Name dollarless_value; Envvar envvar; int exit_status; wchar_t *file_being_read; /* Variable gnu_style=true if env. var. SUN_MAKE_COMPAT_MODE=GNU (RFE 4866328) */ Boolean gnu_style = false; Name_set hashtab; Name host_arch; Name host_mach; int line_number; char *make_state_lockfile; Boolean make_word_mentioned; Makefile_type makefile_type = reading_nothing; char mbs_buffer[(MAXPATHLEN * MB_LEN_MAX)]; Name path_name; Boolean posix = true; Name hat; Name query; Boolean query_mentioned; Boolean reading_environment; Name shell_name; Boolean svr4 = false; Name target_arch; Name target_mach; Boolean tilde_rule; Name virtual_root; Boolean vpath_defined; Name vpath_name; wchar_t wcs_buffer[MAXPATHLEN]; Boolean working_on_targets; Boolean out_err_same; pid_t childPid = -1; // This variable is used for killing child's process // Such as qrsh, running command, etc. /* * timestamps defined in defs.h */ const timestruc_t file_no_time = { -1, 0 }; const timestruc_t file_doesnt_exist = { 0, 0 }; const timestruc_t file_is_dir = { 1, 0 }; const timestruc_t file_min_time = { 2, 0 }; const timestruc_t file_max_time = { INT_MAX, 0 }; /* * 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 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * i18n.cc * * Deal with internationalization conversions */ /* * Included files */ #include #include /* setup_char_semantics() */ /* * get_char_semantics_value(ch) * * Return value: * The character semantics of ch. * * Parameters: * ch character we want semantics for. * */ char get_char_semantics_value(wchar_t ch) { static Boolean char_semantics_setup; if (!char_semantics_setup) { setup_char_semantics(); char_semantics_setup = true; } return char_semantics[get_char_semantics_entry(ch)]; } /* * get_char_semantics_entry(ch) * * Return value: * The slot number in the array for special make chars, * else the slot number of the last array entry. * * Parameters: * ch The wide character * * Global variables used: * char_semantics_char[] array of special wchar_t chars * "&*@`\\|[]:$=!>-\n#()%?;^<'\"" */ int get_char_semantics_entry(wchar_t ch) { wchar_t *char_sem_char; char_sem_char = (wchar_t *) wcschr(char_semantics_char, ch); if (char_sem_char == NULL) { /* * Return the integer entry for the last slot, * whose content is empty. */ return (CHAR_SEMANTICS_ENTRIES - 1); } else { return (char_sem_char - char_semantics_char); } } /* * 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. */ /* * macro.cc * * Handle expansion of make macros */ /* * Included files */ #include /* sh_command2string() */ #include /* get_char_semantics_value() */ #include #include /* retmem() */ #include /* get_next_block_fn() */ #include /* * File table of contents */ static void add_macro_to_global_list(Name macro_to_add); static void expand_value_with_daemon(Name, Property macro, String destination, Boolean cmd); static void init_arch_macros(void); static void init_mach_macros(void); static Boolean init_arch_done = false; static Boolean init_mach_done = false; long env_alloc_num = 0; long env_alloc_bytes = 0; /* * getvar(name) * * Return expanded value of macro. * * Return value: * The expanded value of the macro * * Parameters: * name The name of the macro we want the value for * * Global variables used: */ Name getvar(Name name) { String_rec destination; wchar_t buffer[STRING_BUFFER_LENGTH]; Name result; if ((name == host_arch) || (name == target_arch)) { if (!init_arch_done) { init_arch_done = true; init_arch_macros(); } } if ((name == host_mach) || (name == target_mach)) { if (!init_mach_done) { init_mach_done = true; init_mach_macros(); } } INIT_STRING_FROM_STACK(destination, buffer); expand_value(maybe_append_prop(name, macro_prop)->body.macro.value, &destination, false); result = GETNAME(destination.buffer.start, FIND_LENGTH); if (destination.free_after_use) { retmem(destination.buffer.start); } return result; } /* * expand_value(value, destination, cmd) * * Recursively expands all macros in the string value. * destination is where the expanded value should be appended. * * Parameters: * value The value we are expanding * destination Where to deposit the expansion * cmd If we are evaluating a command line we * turn \ quoting off * * Global variables used: */ void expand_value(Name value, String destination, Boolean cmd) { Source_rec sourceb; Source source = &sourceb; wchar_t *source_p = NULL; wchar_t *source_end = NULL; wchar_t *block_start = NULL; int quote_seen = 0; if (value == NULL) { /* * Make sure to get a string allocated even if it * will be empty. */ MBSTOWCS(wcs_buffer, ""); append_string(wcs_buffer, destination, FIND_LENGTH); destination->text.end = destination->text.p; return; } if (!value->dollar) { /* * If the value we are expanding does not contain * any $, we don't have to parse it. */ APPEND_NAME(value, destination, (int) value->hash.length ); destination->text.end = destination->text.p; return; } if (value->being_expanded) { fatal_reader_mksh(gettext("Loop detected when expanding macro value `%s'"), value->string_mb); } value->being_expanded = true; /* Setup the structure we read from */ Wstring vals(value); sourceb.string.text.p = sourceb.string.buffer.start = wcsdup(vals.get_string()); sourceb.string.free_after_use = true; sourceb.string.text.end = sourceb.string.buffer.end = sourceb.string.text.p + value->hash.length; sourceb.previous = NULL; sourceb.fd = -1; sourceb.inp_buf = sourceb.inp_buf_ptr = sourceb.inp_buf_end = NULL; sourceb.error_converting = false; /* Lift some pointers from the struct to local register variables */ CACHE_SOURCE(0); /* We parse the string in segments */ /* We read chars until we find a $, then we append what we have read so far */ /* (since last $ processing) to the destination. When we find a $ we call */ /* expand_macro() and let it expand that particular $ reference into dest */ block_start = source_p; quote_seen = 0; for (; 1; source_p++) { switch (GET_CHAR()) { case backslash_char: /* Quote $ in macro value */ if (!cmd) { quote_seen = ~quote_seen; } continue; case dollar_char: /* Save the plain string we found since */ /* start of string or previous $ */ if (quote_seen) { append_string(block_start, destination, source_p - block_start - 1); block_start = source_p; break; } append_string(block_start, destination, source_p - block_start); source->string.text.p = ++source_p; UNCACHE_SOURCE(); /* Go expand the macro reference */ expand_macro(source, destination, sourceb.string.buffer.start, cmd); CACHE_SOURCE(1); block_start = source_p + 1; break; case nul_char: /* The string ran out. Get some more */ append_string(block_start, destination, source_p - block_start); GET_NEXT_BLOCK_NOCHK(source); if (source == NULL) { destination->text.end = destination->text.p; value->being_expanded = false; return; } if (source->error_converting) { fatal_reader_mksh("Internal error: Invalid byte sequence in expand_value()"); } block_start = source_p; source_p--; continue; } quote_seen = 0; } retmem(sourceb.string.buffer.start); } /* * expand_macro(source, destination, current_string, cmd) * * Should be called with source->string.text.p pointing to * the first char after the $ that starts a macro reference. * source->string.text.p is returned pointing to the first char after * the macro name. * It will read the macro name, expanding any macros in it, * and get the value. The value is then expanded. * destination is a String that is filled in with the expanded macro. * It may be passed in referencing a buffer to expand the macro into. * Note that most expansions are done on demand, e.g. right * before the command is executed and not while the file is * being parsed. * * Parameters: * source The source block that references the string * to expand * destination Where to put the result * current_string The string we are expanding, for error msg * cmd If we are evaluating a command line we * turn \ quoting off * * Global variables used: * funny Vector of semantic tags for characters * is_conditional Set if a conditional macro is refd * make_word_mentioned Set if the word "MAKE" is mentioned * makefile_type We deliver extra msg when reading makefiles * query The Name "?", compared against * query_mentioned Set if the word "?" is mentioned */ void expand_macro(Source source, String destination, wchar_t *current_string, Boolean cmd) { static Name make = (Name)NULL; static wchar_t colon_sh[4]; static wchar_t colon_shell[7]; String_rec string; wchar_t buffer[STRING_BUFFER_LENGTH]; wchar_t *source_p = source->string.text.p; wchar_t *source_end = source->string.text.end; int closer = 0; wchar_t *block_start = (wchar_t *)NULL; int quote_seen = 0; int closer_level = 1; Name name = (Name)NULL; wchar_t *colon = (wchar_t *)NULL; wchar_t *percent = (wchar_t *)NULL; wchar_t *eq = (wchar_t *) NULL; Property macro = NULL; wchar_t *p = (wchar_t*)NULL; String_rec extracted; wchar_t extracted_string[MAXPATHLEN]; wchar_t *left_head = NULL; wchar_t *left_tail = NULL; wchar_t *right_tail = NULL; int left_head_len = 0; int left_tail_len = 0; int tmp_len = 0; wchar_t *right_hand[128]; int i = 0; enum { no_extract, dir_extract, file_extract } extraction = no_extract; enum { no_replace, suffix_replace, pattern_replace, sh_replace } replacement = no_replace; if (make == NULL) { MBSTOWCS(wcs_buffer, "MAKE"); make = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(colon_sh, ":sh"); MBSTOWCS(colon_shell, ":shell"); } right_hand[0] = NULL; /* First copy the (macro-expanded) macro name into string. */ INIT_STRING_FROM_STACK(string, buffer); recheck_first_char: /* Check the first char of the macro name to figure out what to do. */ switch (GET_CHAR()) { case nul_char: GET_NEXT_BLOCK_NOCHK(source); if (source == NULL) { WCSTOMBS(mbs_buffer, current_string); fatal_reader_mksh(gettext("'$' at end of string `%s'"), mbs_buffer); } if (source->error_converting) { fatal_reader_mksh("Internal error: Invalid byte sequence in expand_macro()"); } goto recheck_first_char; case parenleft_char: /* Multi char name. */ closer = (int) parenright_char; break; case braceleft_char: /* Multi char name. */ closer = (int) braceright_char; break; case newline_char: fatal_reader_mksh(gettext("'$' at end of line")); default: /* Single char macro name. Just suck it up */ append_char(*source_p, &string); source->string.text.p = source_p + 1; goto get_macro_value; } /* Handle multi-char macro names */ block_start = ++source_p; quote_seen = 0; for (; 1; source_p++) { switch (GET_CHAR()) { case nul_char: append_string(block_start, &string, source_p - block_start); GET_NEXT_BLOCK_NOCHK(source); if (source == NULL) { if (current_string != NULL) { WCSTOMBS(mbs_buffer, current_string); fatal_reader_mksh(gettext("Unmatched `%c' in string `%s'"), closer == (int) braceright_char ? (int) braceleft_char : (int) parenleft_char, mbs_buffer); } else { fatal_reader_mksh(gettext("Premature EOF")); } } if (source->error_converting) { fatal_reader_mksh("Internal error: Invalid byte sequence in expand_macro()"); } block_start = source_p; source_p--; continue; case newline_char: fatal_reader_mksh(gettext("Unmatched `%c' on line"), closer == (int) braceright_char ? (int) braceleft_char : (int) parenleft_char); case backslash_char: /* Quote dollar in macro value. */ if (!cmd) { quote_seen = ~quote_seen; } continue; case dollar_char: /* * Macro names may reference macros. * This expands the value of such macros into the * macro name string. */ if (quote_seen) { append_string(block_start, &string, source_p - block_start - 1); block_start = source_p; break; } append_string(block_start, &string, source_p - block_start); source->string.text.p = ++source_p; UNCACHE_SOURCE(); expand_macro(source, &string, current_string, cmd); CACHE_SOURCE(0); block_start = source_p; source_p--; break; case parenleft_char: /* Allow nested pairs of () in the macro name. */ if (closer == (int) parenright_char) { closer_level++; } break; case braceleft_char: /* Allow nested pairs of {} in the macro name. */ if (closer == (int) braceright_char) { closer_level++; } break; case parenright_char: case braceright_char: /* * End of the name. Save the string in the macro * name string. */ if ((*source_p == closer) && (--closer_level <= 0)) { source->string.text.p = source_p + 1; append_string(block_start, &string, source_p - block_start); goto get_macro_value; } break; } quote_seen = 0; } /* * We got the macro name. We now inspect it to see if it * specifies any translations of the value. */ get_macro_value: name = NULL; /* First check if we have a $(@D) type translation. */ if ((get_char_semantics_value(string.buffer.start[0]) & (int) special_macro_sem) && (string.text.p - string.buffer.start >= 2) && ((string.buffer.start[1] == 'D') || (string.buffer.start[1] == 'F'))) { switch (string.buffer.start[1]) { case 'D': extraction = dir_extract; break; case 'F': extraction = file_extract; break; default: WCSTOMBS(mbs_buffer, string.buffer.start); fatal_reader_mksh(gettext("Illegal macro reference `%s'"), mbs_buffer); } /* Internalize the macro name using the first char only. */ name = GETNAME(string.buffer.start, 1); (void) wcscpy(string.buffer.start, string.buffer.start + 2); } /* Check for other kinds of translations. */ if ((colon = (wchar_t *) wcschr(string.buffer.start, (int) colon_char)) != NULL) { /* * We have a $(FOO:.c=.o) type translation. * Get the name of the macro proper. */ if (name == NULL) { name = GETNAME(string.buffer.start, colon - string.buffer.start); } /* Pickup all the translations. */ if (IS_WEQUAL(colon, colon_sh) || IS_WEQUAL(colon, colon_shell)) { replacement = sh_replace; } else if ((svr4) || ((percent = (wchar_t *) wcschr(colon + 1, (int) percent_char)) == NULL)) { while (colon != NULL) { if ((eq = (wchar_t *) wcschr(colon + 1, (int) equal_char)) == NULL) { fatal_reader_mksh(gettext("= missing from replacement macro reference")); } left_tail_len = eq - colon - 1; if(left_tail) { retmem(left_tail); } left_tail = ALLOC_WC(left_tail_len + 1); (void) wcsncpy(left_tail, colon + 1, eq - colon - 1); left_tail[eq - colon - 1] = (int) nul_char; replacement = suffix_replace; if ((colon = (wchar_t *) wcschr(eq + 1, (int) colon_char)) != NULL) { tmp_len = colon - eq; if(right_tail) { retmem(right_tail); } right_tail = ALLOC_WC(tmp_len); (void) wcsncpy(right_tail, eq + 1, colon - eq - 1); right_tail[colon - eq - 1] = (int) nul_char; } else { if(right_tail) { retmem(right_tail); } right_tail = ALLOC_WC(wcslen(eq) + 1); (void) wcscpy(right_tail, eq + 1); } } } else { if ((eq = (wchar_t *) wcschr(colon + 1, (int) equal_char)) == NULL) { fatal_reader_mksh(gettext("= missing from replacement macro reference")); } if ((percent = (wchar_t *) wcschr(colon + 1, (int) percent_char)) == NULL) { fatal_reader_mksh(gettext("%% missing from replacement macro reference")); } if (eq < percent) { fatal_reader_mksh(gettext("%% missing from replacement macro reference")); } if (percent > (colon + 1)) { tmp_len = percent - colon; if(left_head) { retmem(left_head); } left_head = ALLOC_WC(tmp_len); (void) wcsncpy(left_head, colon + 1, percent - colon - 1); left_head[percent-colon-1] = (int) nul_char; left_head_len = percent-colon-1; } else { left_head = NULL; left_head_len = 0; } if (eq > percent+1) { tmp_len = eq - percent; if(left_tail) { retmem(left_tail); } left_tail = ALLOC_WC(tmp_len); (void) wcsncpy(left_tail, percent + 1, eq - percent - 1); left_tail[eq-percent-1] = (int) nul_char; left_tail_len = eq-percent-1; } else { left_tail = NULL; left_tail_len = 0; } if ((percent = (wchar_t *) wcschr(++eq, (int) percent_char)) == NULL) { right_hand[0] = ALLOC_WC(wcslen(eq) + 1); right_hand[1] = NULL; (void) wcscpy(right_hand[0], eq); } else { i = 0; do { right_hand[i] = ALLOC_WC(percent-eq+1); (void) wcsncpy(right_hand[i], eq, percent - eq); right_hand[i][percent-eq] = (int) nul_char; if (i++ >= VSIZEOF(right_hand)) { fatal_mksh(gettext("Too many %% in pattern")); } eq = percent + 1; if (eq[0] == (int) nul_char) { MBSTOWCS(wcs_buffer, ""); right_hand[i] = (wchar_t *) wcsdup(wcs_buffer); i++; break; } } while ((percent = (wchar_t *) wcschr(eq, (int) percent_char)) != NULL); if (eq[0] != (int) nul_char) { right_hand[i] = ALLOC_WC(wcslen(eq) + 1); (void) wcscpy(right_hand[i], eq); i++; } right_hand[i] = NULL; } replacement = pattern_replace; } } if (name == NULL) { /* * No translations found. * Use the whole string as the macro name. */ name = GETNAME(string.buffer.start, string.text.p - string.buffer.start); } if (string.free_after_use) { retmem(string.buffer.start); } if (name == make) { make_word_mentioned = true; } if (name == query) { query_mentioned = true; } if ((name == host_arch) || (name == target_arch)) { if (!init_arch_done) { init_arch_done = true; init_arch_macros(); } } if ((name == host_mach) || (name == target_mach)) { if (!init_mach_done) { init_mach_done = true; init_mach_macros(); } } /* Get the macro value. */ macro = get_prop(name->prop, macro_prop); if ((macro != NULL) && macro->body.macro.is_conditional) { conditional_macro_used = true; /* * Add this conditional macro to the beginning of the * global list. */ add_macro_to_global_list(name); if (makefile_type == reading_makefile) { warning_mksh(gettext("Conditional macro `%s' referenced in file `%ws', line %d"), name->string_mb, file_being_read, line_number); } } /* Macro name read and parsed. Expand the value. */ if ((macro == NULL) || (macro->body.macro.value == NULL)) { /* If the value is empty, we just get out of here. */ goto exit; } if (replacement == sh_replace) { /* If we should do a :sh transform, we expand the command * and process it. */ INIT_STRING_FROM_STACK(string, buffer); /* Expand the value into a local string buffer and run cmd. */ expand_value_with_daemon(name, macro, &string, cmd); sh_command2string(&string, destination); } else if ((replacement != no_replace) || (extraction != no_extract)) { /* * If there were any transforms specified in the macro * name, we deal with them here. */ INIT_STRING_FROM_STACK(string, buffer); /* Expand the value into a local string buffer. */ expand_value_with_daemon(name, macro, &string, cmd); /* Scan the expanded string. */ p = string.buffer.start; while (*p != (int) nul_char) { wchar_t chr; /* * First skip over any white space and append * that to the destination string. */ block_start = p; while ((*p != (int) nul_char) && iswspace(*p)) { p++; } append_string(block_start, destination, p - block_start); /* Then find the end of the next word. */ block_start = p; while ((*p != (int) nul_char) && !iswspace(*p)) { p++; } /* If we cant find another word we are done */ if (block_start == p) { break; } /* Then apply the transforms to the word */ INIT_STRING_FROM_STACK(extracted, extracted_string); switch (extraction) { case dir_extract: /* * $(@D) type transform. Extract the * path from the word. Deliver "." if * none is found. */ if (p != NULL) { chr = *p; *p = (int) nul_char; } eq = (wchar_t *) wcsrchr(block_start, (int) slash_char); if (p != NULL) { *p = chr; } if ((eq == NULL) || (eq > p)) { MBSTOWCS(wcs_buffer, "."); append_string(wcs_buffer, &extracted, 1); } else { append_string(block_start, &extracted, eq - block_start); } break; case file_extract: /* * $(@F) type transform. Remove the path * from the word if any. */ if (p != NULL) { chr = *p; *p = (int) nul_char; } eq = (wchar_t *) wcsrchr(block_start, (int) slash_char); if (p != NULL) { *p = chr; } if ((eq == NULL) || (eq > p)) { append_string(block_start, &extracted, p - block_start); } else { append_string(eq + 1, &extracted, p - eq - 1); } break; case no_extract: append_string(block_start, &extracted, p - block_start); break; } switch (replacement) { case suffix_replace: /* * $(FOO:.o=.c) type transform. * Maybe replace the tail of the word. */ if (((extracted.text.p - extracted.buffer.start) >= left_tail_len) && IS_WEQUALN(extracted.text.p - left_tail_len, left_tail, left_tail_len)) { append_string(extracted.buffer.start, destination, (extracted.text.p - extracted.buffer.start) - left_tail_len); append_string(right_tail, destination, FIND_LENGTH); } else { append_string(extracted.buffer.start, destination, FIND_LENGTH); } break; case pattern_replace: /* $(X:a%b=c%d) type transform. */ if (((extracted.text.p - extracted.buffer.start) >= left_head_len+left_tail_len) && IS_WEQUALN(left_head, extracted.buffer.start, left_head_len) && IS_WEQUALN(left_tail, extracted.text.p - left_tail_len, left_tail_len)) { i = 0; while (right_hand[i] != NULL) { append_string(right_hand[i], destination, FIND_LENGTH); i++; if (right_hand[i] != NULL) { append_string(extracted.buffer. start + left_head_len, destination, (extracted.text.p - extracted.buffer.start)-left_head_len-left_tail_len); } } } else { append_string(extracted.buffer.start, destination, FIND_LENGTH); } break; case no_replace: append_string(extracted.buffer.start, destination, FIND_LENGTH); break; case sh_replace: break; } } if (string.free_after_use) { retmem(string.buffer.start); } } else { /* * This is for the case when the macro name did not * specify transforms. */ if (!strncmp(name->string_mb, "GET", 3)) { dollarget_seen = true; } dollarless_flag = false; if (!strncmp(name->string_mb, "<", 1) && dollarget_seen) { dollarless_flag = true; dollarget_seen = false; } expand_value_with_daemon(name, macro, destination, cmd); } exit: if(left_tail) { retmem(left_tail); } if(right_tail) { retmem(right_tail); } if(left_head) { retmem(left_head); } i = 0; while (right_hand[i] != NULL) { retmem(right_hand[i]); i++; } *destination->text.p = (int) nul_char; destination->text.end = destination->text.p; } static void add_macro_to_global_list(Name macro_to_add) { Macro_list new_macro; Macro_list macro_on_list; char *name_on_list = (char*)NULL; char *name_to_add = macro_to_add->string_mb; char *value_on_list = (char*)NULL; const char *value_to_add = (char*)NULL; if (macro_to_add->prop->body.macro.value != NULL) { value_to_add = macro_to_add->prop->body.macro.value->string_mb; } else { value_to_add = ""; } /* * Check if this macro is already on list, if so, do nothing */ for (macro_on_list = cond_macro_list; macro_on_list != NULL; macro_on_list = macro_on_list->next) { name_on_list = macro_on_list->macro_name; value_on_list = macro_on_list->value; if (IS_EQUAL(name_on_list, name_to_add)) { if (IS_EQUAL(value_on_list, value_to_add)) { return; } } } new_macro = (Macro_list) malloc(sizeof(Macro_list_rec)); new_macro->macro_name = strdup(name_to_add); new_macro->value = strdup(value_to_add); new_macro->next = cond_macro_list; cond_macro_list = new_macro; } /* * init_arch_macros(void) * * Set the magic macros TARGET_ARCH, HOST_ARCH, * * Parameters: * * Global variables used: * host_arch Property for magic macro HOST_ARCH * target_arch Property for magic macro TARGET_ARCH * * Return value: * The function does not return a value, but can * call fatal() in case of error. */ static void init_arch_macros(void) { String_rec result_string; wchar_t wc_buf[STRING_BUFFER_LENGTH]; char mb_buf[STRING_BUFFER_LENGTH]; FILE *pipe; Name value; int set_host, set_target; const char *mach_command = "/bin/mach"; set_host = (get_prop(host_arch->prop, macro_prop) == NULL); set_target = (get_prop(target_arch->prop, macro_prop) == NULL); if (set_host || set_target) { INIT_STRING_FROM_STACK(result_string, wc_buf); append_char((int) hyphen_char, &result_string); if ((pipe = popen(mach_command, "r")) == NULL) { fatal_mksh(gettext("Execute of %s failed"), mach_command); } while (fgets(mb_buf, sizeof(mb_buf), pipe) != NULL) { MBSTOWCS(wcs_buffer, mb_buf); append_string(wcs_buffer, &result_string, wcslen(wcs_buffer)); } if (pclose(pipe) != 0) { fatal_mksh(gettext("Execute of %s failed"), mach_command); } value = GETNAME(result_string.buffer.start, wcslen(result_string.buffer.start)); if (set_host) { (void) setvar_daemon(host_arch, value, false, no_daemon, true, 0); } if (set_target) { (void) setvar_daemon(target_arch, value, false, no_daemon, true, 0); } } } /* * init_mach_macros(void) * * Set the magic macros TARGET_MACH, HOST_MACH, * * Parameters: * * Global variables used: * host_mach Property for magic macro HOST_MACH * target_mach Property for magic macro TARGET_MACH * * Return value: * The function does not return a value, but can * call fatal() in case of error. */ static void init_mach_macros(void) { String_rec result_string; wchar_t wc_buf[STRING_BUFFER_LENGTH]; char mb_buf[STRING_BUFFER_LENGTH]; FILE *pipe; Name value; int set_host, set_target; const char *arch_command = "/bin/arch"; set_host = (get_prop(host_mach->prop, macro_prop) == NULL); set_target = (get_prop(target_mach->prop, macro_prop) == NULL); if (set_host || set_target) { INIT_STRING_FROM_STACK(result_string, wc_buf); append_char((int) hyphen_char, &result_string); if ((pipe = popen(arch_command, "r")) == NULL) { fatal_mksh(gettext("Execute of %s failed"), arch_command); } while (fgets(mb_buf, sizeof(mb_buf), pipe) != NULL) { MBSTOWCS(wcs_buffer, mb_buf); append_string(wcs_buffer, &result_string, wcslen(wcs_buffer)); } if (pclose(pipe) != 0) { fatal_mksh(gettext("Execute of %s failed"), arch_command); } value = GETNAME(result_string.buffer.start, wcslen(result_string.buffer.start)); if (set_host) { (void) setvar_daemon(host_mach, value, false, no_daemon, true, 0); } if (set_target) { (void) setvar_daemon(target_mach, value, false, no_daemon, true, 0); } } } /* * expand_value_with_daemon(name, macro, destination, cmd) * * Checks for daemons and then maybe calls expand_value(). * * Parameters: * name Name of the macro (Added by the NSE) * macro The property block with the value to expand * destination Where the result should be deposited * cmd If we are evaluating a command line we * turn \ quoting off * * Global variables used: */ static void expand_value_with_daemon(Name, Property macro, String destination, Boolean cmd) { Chain chain; switch (macro->body.macro.daemon) { case no_daemon: if (!svr4 && !posix) { expand_value(macro->body.macro.value, destination, cmd); } else { if (dollarless_flag && tilde_rule) { expand_value(dollarless_value, destination, cmd); dollarless_flag = false; tilde_rule = false; } else { expand_value(macro->body.macro.value, destination, cmd); } } return; case chain_daemon: /* If this is a $? value we call the daemon to translate the */ /* list of names to a string */ for (chain = (Chain) macro->body.macro.value; chain != NULL; chain = chain->next) { APPEND_NAME(chain->name, destination, (int) chain->name->hash.length); if (chain->next != NULL) { append_char((int) space_char, destination); } } return; } } /* * We use a permanent buffer to reset SUNPRO_DEPENDENCIES value. */ char *sunpro_dependencies_buf = NULL; char *sunpro_dependencies_oldbuf = NULL; int sunpro_dependencies_buf_size = 0; /* * setvar_daemon(name, value, append, daemon, strip_trailing_spaces) * * Set a macro value, possibly supplying a daemon to be used * when referencing the value. * * Return value: * The property block with the new value * * Parameters: * name Name of the macro to set * value The value to set * append Should we reset or append to the current value? * daemon Special treatment when reading the value * strip_trailing_spaces from the end of value->string * debug_level Indicates how much tracing we should do * * Global variables used: * makefile_type Used to check if we should enforce read only * path_name The Name "PATH", compared against * virtual_root The Name "VIRTUAL_ROOT", compared against * vpath_defined Set if the macro VPATH is set * vpath_name The Name "VPATH", compared against * envvar A list of environment vars with $ in value */ Property setvar_daemon(Name name, Name value, Boolean append, Daemon daemon, Boolean strip_trailing_spaces, short debug_level) { Property macro = maybe_append_prop(name, macro_prop); Property macro_apx = get_prop(name->prop, macro_append_prop); int length = 0; String_rec destination; wchar_t buffer[STRING_BUFFER_LENGTH]; Chain chain; Name val; wchar_t *val_string = (wchar_t*)NULL; Wstring wcb; if ((makefile_type != reading_nothing) && macro->body.macro.read_only) { return macro; } /* Strip spaces from the end of the value */ if (daemon == no_daemon) { if(value != NULL) { wcb.init(value); length = wcb.length(); val_string = wcb.get_string(); } if ((length > 0) && iswspace(val_string[length-1])) { INIT_STRING_FROM_STACK(destination, buffer); buffer[0] = 0; append_string(val_string, &destination, length); if (strip_trailing_spaces) { while ((length > 0) && iswspace(destination.buffer.start[length-1])) { destination.buffer.start[--length] = 0; } } value = GETNAME(destination.buffer.start, FIND_LENGTH); } } if(macro_apx != NULL) { val = macro_apx->body.macro_appendix.value; } else { val = macro->body.macro.value; } if (append) { /* * If we are appending, we just tack the new value after * the old one with a space in between. */ INIT_STRING_FROM_STACK(destination, buffer); buffer[0] = 0; if ((macro != NULL) && (val != NULL)) { APPEND_NAME(val, &destination, (int) val->hash.length); if (value != NULL) { wcb.init(value); if(wcb.length() > 0) { MBTOWC(wcs_buffer, " "); append_char(wcs_buffer[0], &destination); } } } if (value != NULL) { APPEND_NAME(value, &destination, (int) value->hash.length); } value = GETNAME(destination.buffer.start, FIND_LENGTH); wcb.init(value); if (destination.free_after_use) { retmem(destination.buffer.start); } } /* Debugging trace */ if (debug_level > 1) { if (value != NULL) { switch (daemon) { case chain_daemon: (void) printf("%s =", name->string_mb); for (chain = (Chain) value; chain != NULL; chain = chain->next) { (void) printf(" %s", chain->name->string_mb); } (void) printf("\n"); break; case no_daemon: (void) printf("%s= %s\n", name->string_mb, value->string_mb); break; } } else { (void) printf("%s =\n", name->string_mb); } } /* Set the new values in the macro property block */ /**/ if(macro_apx != NULL) { macro_apx->body.macro_appendix.value = value; INIT_STRING_FROM_STACK(destination, buffer); buffer[0] = 0; if (value != NULL) { APPEND_NAME(value, &destination, (int) value->hash.length); if (macro_apx->body.macro_appendix.value_to_append != NULL) { MBTOWC(wcs_buffer, " "); append_char(wcs_buffer[0], &destination); } } if (macro_apx->body.macro_appendix.value_to_append != NULL) { APPEND_NAME(macro_apx->body.macro_appendix.value_to_append, &destination, (int) macro_apx->body.macro_appendix.value_to_append->hash.length); } value = GETNAME(destination.buffer.start, FIND_LENGTH); if (destination.free_after_use) { retmem(destination.buffer.start); } } /**/ macro->body.macro.value = value; macro->body.macro.daemon = daemon; /* * If the user changes the VIRTUAL_ROOT, we need to flush * the vroot package cache. */ if (name == path_name) { flush_path_cache(); } if (name == virtual_root) { flush_vroot_cache(); } /* If this sets the VPATH we remember that */ if ((name == vpath_name) && (value != NULL) && (value->hash.length > 0)) { vpath_defined = true; } /* * For environment variables we also set the * environment value each time. */ if (macro->body.macro.exported) { static char *env; if (!reading_environment && (value != NULL)) { Envvar p; for (p = envvar; p != NULL; p = p->next) { if (p->name == name) { p->value = value; p->already_put = false; goto found_it; } } p = ALLOC(Envvar); p->name = name; p->value = value; p->next = envvar; p->env_string = NULL; p->already_put = false; envvar = p; found_it:; } if (reading_environment || (value == NULL) || !value->dollar) { length = 2 + strlen(name->string_mb); if (value != NULL) { length += strlen(value->string_mb); } Property env_prop = maybe_append_prop(name, env_mem_prop); /* * We use a permanent buffer to reset SUNPRO_DEPENDENCIES value. */ if (!strncmp(name->string_mb, "SUNPRO_DEPENDENCIES", 19)) { if (length >= sunpro_dependencies_buf_size) { sunpro_dependencies_buf_size=length*2; if (sunpro_dependencies_buf_size < 4096) sunpro_dependencies_buf_size = 4096; // Default minimum size if (sunpro_dependencies_buf) sunpro_dependencies_oldbuf = sunpro_dependencies_buf; sunpro_dependencies_buf=getmem(sunpro_dependencies_buf_size); } env = sunpro_dependencies_buf; } else { env = getmem(length); } env_alloc_num++; env_alloc_bytes += length; (void) sprintf(env, "%s=%s", name->string_mb, value == NULL ? "" : value->string_mb); (void) putenv(env); env_prop->body.env_mem.value = env; if (sunpro_dependencies_oldbuf) { /* Return old buffer */ retmem_mb(sunpro_dependencies_oldbuf); sunpro_dependencies_oldbuf = NULL; } } } if (name == target_arch) { Name ha = getvar(host_arch); Name ta = getvar(target_arch); Name vr = getvar(virtual_root); int length; wchar_t *new_value; wchar_t *old_vr; Boolean new_value_allocated = false; Wstring ha_str(ha); Wstring ta_str(ta); Wstring vr_str(vr); wchar_t * wcb_ha = ha_str.get_string(); wchar_t * wcb_ta = ta_str.get_string(); wchar_t * wcb_vr = vr_str.get_string(); length = 32 + wcslen(wcb_ha) + wcslen(wcb_ta) + wcslen(wcb_vr); old_vr = wcb_vr; MBSTOWCS(wcs_buffer, "/usr/arch/"); if (IS_WEQUALN(old_vr, wcs_buffer, wcslen(wcs_buffer))) { old_vr = (wchar_t *) wcschr(old_vr, (int) colon_char) + 1; } if ( (ha == ta) || (wcslen(wcb_ta) == 0) ) { new_value = old_vr; } else { new_value = ALLOC_WC(length); new_value_allocated = true; WCSTOMBS(mbs_buffer, old_vr); (void) swprintf(new_value, length * SIZEOFWCHAR_T, L"/usr/arch/%s/%s:%s", ha->string_mb + 1, ta->string_mb + 1, mbs_buffer); } if (new_value[0] != 0) { (void) setvar_daemon(virtual_root, GETNAME(new_value, FIND_LENGTH), false, no_daemon, true, debug_level); } if (new_value_allocated) { retmem(new_value); } } return macro; } /* * 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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * misc.cc * * This file contains various unclassified routines. Some main groups: * getname * Memory allocation * String handling * Property handling * Error message handling * Make internal state dumping * main routine support */ /* * Included files */ #include /* bsd_signal() */ #include /* get_char_semantics_value() */ #include #include /* va_list, va_start(), va_end() */ #include /* mbstowcs() */ #include /* SIG_DFL */ #include /* wait() */ #include /* strerror() */ #include /* * Defined macros */ /* * typedefs & structs */ /* * Static variables */ extern "C" { void (*sigivalue)(int) = SIG_DFL; void (*sigqvalue)(int) = SIG_DFL; void (*sigtvalue)(int) = SIG_DFL; void (*sighvalue)(int) = SIG_DFL; } long getname_bytes_count = 0; long getname_names_count = 0; long getname_struct_count = 0; long freename_bytes_count = 0; long freename_names_count = 0; long freename_struct_count = 0; long expandstring_count = 0; long getwstring_count = 0; /* * File table of contents */ static void expand_string(String string, int length); #define FATAL_ERROR_MSG_SIZE 200 /* * getmem(size) * * malloc() version that checks the returned value. * * Return value: * The memory chunk we allocated * * Parameters: * size The size of the chunk we need * * Global variables used: */ char * getmem(size_t size) { char *result = (char *)malloc(size); if (result == NULL) { (void) fprintf(stderr, "*** Error: malloc(%d) failed: %s\n%s", size, strerror(errno), gettext("mksh: Fatal error: Out of memory\n")); exit(1); } return (result); } /* * retmem(p) * * Cover funtion for free() to make it possible to insert advises. * * Parameters: * p The memory block to free * * Global variables used: */ void retmem(wchar_t *p) { (void) free((char *) p); } void retmem_mb(caddr_t p) { (void) free(p); } /* * getname_fn(name, len, dont_enter) * * Hash a name string to the corresponding nameblock. * * Return value: * The Name block for the string * * Parameters: * name The string we want to internalize * len The length of that string * dont_enter Don't enter the name if it does not exist * * Global variables used: * funny The vector of semantic tags for characters * hashtab The hashtable used for the nametable */ Name getname_fn(wchar_t *name, int len, Boolean dont_enter, Boolean * foundp) { int length; wchar_t *cap = name; Name np; static Name_rec empty_Name; char *tmp_mbs_buffer = NULL; char *mbs_name = mbs_buffer; /* * First figure out how long the string is. * If the len argument is -1 we count the chars here. */ if (len == FIND_LENGTH) { length = wcslen(name); } else { length = len; } Wstring ws; ws.init(name, length); if (length >= MAXPATHLEN) { mbs_name = tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); } (void) wcstombs(mbs_name, ws.get_string(), (length * MB_LEN_MAX) + 1); /* Look for the string */ if (dont_enter || (foundp != 0)) { np = hashtab.lookup(mbs_name); if (foundp != 0) { *foundp = (np != 0) ? true : false; } if ((np != 0) || dont_enter) { if(tmp_mbs_buffer != NULL) { retmem_mb(tmp_mbs_buffer); } return np; } else { np = ALLOC(Name); } } else { Boolean found; np = hashtab.insert(mbs_name, found); if (found) { if(tmp_mbs_buffer != NULL) { retmem_mb(tmp_mbs_buffer); } return np; } } getname_struct_count += sizeof(struct _Name); *np = empty_Name; np->string_mb = strdup(mbs_name); if(tmp_mbs_buffer != NULL) { retmem_mb(tmp_mbs_buffer); mbs_name = tmp_mbs_buffer = NULL; } getname_bytes_count += strlen(np->string_mb) + 1; /* Fill in the new Name */ np->stat.time = file_no_time; np->hash.length = length; /* Scan the namestring to classify it */ for (cap = name, len = 0; --length >= 0;) { len |= get_char_semantics_value(*cap++); } np->dollar = BOOLEAN((len & (int) dollar_sem) != 0); np->meta = BOOLEAN((len & (int) meta_sem) != 0); np->percent = BOOLEAN((len & (int) percent_sem) != 0); np->wildcard = BOOLEAN((len & (int) wildcard_sem) != 0); np->colon = BOOLEAN((len & (int) colon_sem) != 0); np->parenleft = BOOLEAN((len & (int) parenleft_sem) != 0); getname_names_count++; return np; } void store_name(Name name) { hashtab.insert(name); } void free_name(Name name) { freename_names_count++; freename_struct_count += sizeof(struct _Name); freename_bytes_count += strlen(name->string_mb) + 1; retmem_mb(name->string_mb); for (Property next, p = name->prop; p != NULL; p = next) { next = p->next; free(p); } free(name); } /* * enable_interrupt(handler) * * This routine sets a new interrupt handler for the signals make * wants to deal with. * * Parameters: * handler The function installed as interrupt handler * * Static variables used: * sigivalue The original signal handler * sigqvalue The original signal handler * sigtvalue The original signal handler * sighvalue The original signal handler */ void enable_interrupt(void (*handler) (int)) { if (sigivalue != SIG_IGN) { (void) bsd_signal(SIGINT, (SIG_PF) handler); } if (sigqvalue != SIG_IGN) { (void) bsd_signal(SIGQUIT, (SIG_PF) handler); } if (sigtvalue != SIG_IGN) { (void) bsd_signal(SIGTERM, (SIG_PF) handler); } if (sighvalue != SIG_IGN) { (void) bsd_signal(SIGHUP, (SIG_PF) handler); } } /* * setup_char_semantics() * * Load the vector char_semantics[] with lexical markers * * Parameters: * * Global variables used: * char_semantics The vector of character semantics that we set */ void setup_char_semantics(void) { const char *s; wchar_t wc_buffer[1]; int entry; if (svr4) { s = "@-"; } else { s = "=@-?!+"; } for (s; MBTOWC(wc_buffer, s); s++) { entry = get_char_semantics_entry(*wc_buffer); char_semantics[entry] |= (int) command_prefix_sem; } char_semantics[dollar_char_entry] |= (int) dollar_sem; for (s = "#|=^();&<>*?[]:$`'\"\\\n"; MBTOWC(wc_buffer, s); s++) { entry = get_char_semantics_entry(*wc_buffer); char_semantics[entry] |= (int) meta_sem; } char_semantics[percent_char_entry] |= (int) percent_sem; for (s = "@*<%?^"; MBTOWC(wc_buffer, s); s++) { entry = get_char_semantics_entry(*wc_buffer); char_semantics[entry] |= (int) special_macro_sem; } for (s = "?[*"; MBTOWC(wc_buffer, s); s++) { entry = get_char_semantics_entry(*wc_buffer); char_semantics[entry] |= (int) wildcard_sem; } char_semantics[colon_char_entry] |= (int) colon_sem; char_semantics[parenleft_char_entry] |= (int) parenleft_sem; } /* * errmsg(errnum) * * Return the error message for a system call error * * Return value: * An error message string * * Parameters: * errnum The number of the error we want to describe */ char * errmsg(int errnum) { char *msg; char *errbuf; errno = 0; msg = strerror(errnum); if (errno == EINVAL) { size_t size = 6 + 1 + 11 + 1; errbuf = getmem(size); (void) snprintf(errbuf, size, gettext("Error %d"), errnum); return (errbuf); } return (msg); } static char static_buf[MAXPATHLEN*3]; /* * fatal_mksh(format, args...) * * Print a message and die * * Parameters: * format printf type format string * args Arguments to match the format */ /*VARARGS*/ void fatal_mksh(const char *message, ...) { va_list args; char *buf = static_buf; char *mksh_fat_err = gettext("mksh: Fatal error: "); char *cur_wrk_dir = gettext("Current working directory: "); int mksh_fat_err_len = strlen(mksh_fat_err); va_start(args, message); (void) fflush(stdout); (void) strcpy(buf, mksh_fat_err); size_t buf_len = vsnprintf(static_buf + mksh_fat_err_len, sizeof(static_buf) - mksh_fat_err_len, message, args) + mksh_fat_err_len + strlen(cur_wrk_dir) + strlen(get_current_path_mksh()) + 3; // "\n\n" va_end(args); if (buf_len >= sizeof(static_buf)) { buf = getmem(buf_len); (void) strcpy(buf, mksh_fat_err); va_start(args, message); (void) vsprintf(buf + mksh_fat_err_len, message, args); va_end(args); } (void) strcat(buf, "\n"); /* if (report_pwd) { */ if (1) { (void) strcat(buf, cur_wrk_dir); (void) strcat(buf, get_current_path_mksh()); (void) strcat(buf, "\n"); } (void) fputs(buf, stderr); (void) fflush(stderr); if (buf != static_buf) { retmem_mb(buf); } exit_status = 1; exit(1); } /* * fatal_reader_mksh(format, args...) * * Parameters: * format printf style format string * args arguments to match the format */ /*VARARGS*/ void fatal_reader_mksh(const char * pattern, ...) { va_list args; char message[1000]; va_start(args, pattern); /* if (file_being_read != NULL) { WCSTOMBS(mbs_buffer, file_being_read); if (line_number != 0) { (void) sprintf(message, gettext("%s, line %d: %s"), mbs_buffer, line_number, pattern); } else { (void) sprintf(message, "%s: %s", mbs_buffer, pattern); } pattern = message; } */ (void) fflush(stdout); (void) fprintf(stderr, gettext("mksh: Fatal error in reader: ")); (void) vfprintf(stderr, pattern, args); (void) fprintf(stderr, "\n"); va_end(args); /* if (temp_file_name != NULL) { (void) fprintf(stderr, gettext("mksh: Temp-file %s not removed\n"), temp_file_name->string_mb); temp_file_name = NULL; } */ /* if (report_pwd) { */ if (1) { (void) fprintf(stderr, gettext("Current working directory %s\n"), get_current_path_mksh()); } (void) fflush(stderr); exit_status = 1; exit(1); } /* * warning_mksh(format, args...) * * Print a message and continue. * * Parameters: * format printf type format string * args Arguments to match the format */ /*VARARGS*/ void warning_mksh(char * message, ...) { va_list args; va_start(args, message); (void) fflush(stdout); (void) fprintf(stderr, gettext("mksh: Warning: ")); (void) vfprintf(stderr, message, args); (void) fprintf(stderr, "\n"); va_end(args); /* if (report_pwd) { */ if (1) { (void) fprintf(stderr, gettext("Current working directory %s\n"), get_current_path_mksh()); } (void) fflush(stderr); } /* * get_current_path_mksh() * * Stuff current_path with the current path if it isnt there already. * * Parameters: * * Global variables used: */ char * get_current_path_mksh(void) { char pwd[(MAXPATHLEN * MB_LEN_MAX)]; static char *current_path; if (current_path == NULL) { getcwd(pwd, sizeof(pwd)); if (pwd[0] == (int) nul_char) { pwd[0] = (int) slash_char; pwd[1] = (int) nul_char; } current_path = strdup(pwd); } return current_path; } /* * append_prop(target, type) * * Create a new property and append it to the property list of a Name. * * Return value: * A new property block for the target * * Parameters: * target The target that wants a new property * type The type of property being requested * * Global variables used: */ Property append_prop(Name target, Property_id type) { Property *insert = &target->prop; Property prop = *insert; int size; switch (type) { case conditional_prop: size = sizeof (struct Conditional); break; case line_prop: size = sizeof (struct Line); break; case macro_prop: size = sizeof (struct _Macro); break; case makefile_prop: size = sizeof (struct Makefile); break; case member_prop: size = sizeof (struct Member); break; case recursive_prop: size = sizeof (struct Recursive); break; case sccs_prop: size = sizeof (struct Sccs); break; case suffix_prop: size = sizeof (struct Suffix); break; case target_prop: size = sizeof (struct Target); break; case time_prop: size = sizeof (struct STime); break; case vpath_alias_prop: size = sizeof (struct Vpath_alias); break; case long_member_name_prop: size = sizeof (struct Long_member_name); break; case macro_append_prop: size = sizeof (struct _Macro_appendix); break; case env_mem_prop: size = sizeof (struct _Env_mem); break; default: fatal_mksh(gettext("Internal error. Unknown prop type %d"), type); } for (; prop != NULL; insert = &prop->next, prop = *insert); size += PROPERTY_HEAD_SIZE; *insert = prop = (Property) getmem(size); memset((char *) prop, 0, size); prop->type = type; prop->next = NULL; return prop; } /* * maybe_append_prop(target, type) * * Append a property to the Name if none of this type exists * else return the one already there * * Return value: * A property of the requested type for the target * * Parameters: * target The target that wants a new property * type The type of property being requested * * Global variables used: */ Property maybe_append_prop(Name target, Property_id type) { Property prop; if ((prop = get_prop(target->prop, type)) != NULL) { return prop; } return append_prop(target, type); } /* * get_prop(start, type) * * Scan the property list of a Name to find the next property * of a given type. * * Return value: * The first property of the type, if any left * * Parameters: * start The first property block to check for type * type The type of property block we need * * Global variables used: */ Property get_prop(Property start, Property_id type) { for (; start != NULL; start = start->next) { if (start->type == type) { return start; } } return NULL; } /* * append_string(from, to, length) * * Append a C string to a make string expanding it if nessecary * * Parameters: * from The source (C style) string * to The destination (make style) string * length The length of the from string * * Global variables used: */ void append_string(wchar_t *from, String to, int length) { if (length == FIND_LENGTH) { length = wcslen(from); } if (to->buffer.start == NULL) { expand_string(to, 32 + length); } if (to->buffer.end - to->text.p <= length) { expand_string(to, (to->buffer.end - to->buffer.start) * 2 + length); } if (length > 0) { (void) wcsncpy(to->text.p, from, length); to->text.p += length; } *(to->text.p) = (int) nul_char; } wchar_t * get_wstring(char *from) { if(from == NULL) { return NULL; } getwstring_count++; wchar_t * wcbuf = ALLOC_WC(strlen(from) + 1); mbstowcs(wcbuf, from, strlen(from)+1); return wcbuf; } void append_string(char *from, String to, int length) { if (length == FIND_LENGTH) { length = strlen(from); } if (to->buffer.start == NULL) { expand_string(to, 32 + length); } if (to->buffer.end - to->text.p <= length) { expand_string(to, (to->buffer.end - to->buffer.start) * 2 + length); } if (length > 0) { (void) mbstowcs(to->text.p, from, length); to->text.p += length; } *(to->text.p) = (int) nul_char; } /* * expand_string(string, length) * * Allocate more memory for strings that run out of space. * * Parameters: * string The make style string we want to expand * length The new length we need * * Global variables used: */ static void expand_string(String string, int length) { wchar_t *p; if (string->buffer.start == NULL) { /* For strings that have no memory allocated */ string->buffer.start = string->text.p = string->text.end = ALLOC_WC(length); string->buffer.end = string->buffer.start + length; string->text.p[0] = (int) nul_char; string->free_after_use = true; expandstring_count++; return; } if (string->buffer.end - string->buffer.start >= length) { /* If we really don't need more memory. */ return; } /* * Get more memory, copy the string and free the old buffer if * it is was malloc()'ed. */ expandstring_count++; p = ALLOC_WC(length); (void) wcscpy(p, string->buffer.start); string->text.p = p + (string->text.p - string->buffer.start); string->text.end = p + (string->text.end - string->buffer.start); string->buffer.end = p + length; if (string->free_after_use) { retmem(string->buffer.start); } string->buffer.start = p; string->free_after_use = true; } /* * append_char(from, to) * * Append one char to a make string expanding it if nessecary * * Parameters: * from Single character to append to string * to The destination (make style) string * * Global variables used: */ void append_char(wchar_t from, String to) { if (to->buffer.start == NULL) { expand_string(to, 32); } if (to->buffer.end - to->text.p <= 2) { expand_string(to, to->buffer.end - to->buffer.start + 32); } *(to->text.p)++ = from; *(to->text.p) = (int) nul_char; } /* * handle_interrupt_mksh() * * This is where C-C traps are caught. */ void handle_interrupt_mksh(int) { (void) fflush(stdout); /* Make sure the processes running under us terminate first. */ if (childPid > 0) { kill(childPid, SIGTERM); childPid = -1; } while (wait((int *) NULL) != -1); exit_status = 2; exit(2); } /* * setup_interrupt() * * This routine saves the original interrupt handler pointers * * Parameters: * * Static variables used: * sigivalue The original signal handler * sigqvalue The original signal handler * sigtvalue The original signal handler * sighvalue The original signal handler */ void setup_interrupt(void (*handler) (int)) { sigivalue = bsd_signal(SIGINT, SIG_IGN); sigqvalue = bsd_signal(SIGQUIT, SIG_IGN); sigtvalue = bsd_signal(SIGTERM, SIG_IGN); sighvalue = bsd_signal(SIGHUP, SIG_IGN); enable_interrupt(handler); } void mbstowcs_with_check(wchar_t *pwcs, const char *s, size_t n) { if(mbstowcs(pwcs, s, n) == -1) { fatal_mksh(gettext("The string `%s' is not valid in current locale"), s); } } Wstring::Wstring() { INIT_STRING_FROM_STACK(string, string_buf); } Wstring::Wstring(struct _Name * name) { INIT_STRING_FROM_STACK(string, string_buf); append_string(name->string_mb, &string, name->hash.length); } Wstring::~Wstring() { if(string.free_after_use) { retmem(string.buffer.start); } } void Wstring::init(struct _Name * name) { if(string.free_after_use) { retmem(string.buffer.start); } INIT_STRING_FROM_STACK(string, string_buf); append_string(name->string_mb, &string, name->hash.length); } void Wstring::init(wchar_t * name, unsigned length) { INIT_STRING_FROM_STACK(string, string_buf); append_string(name, &string, length); string.buffer.start[length] = 0; } Boolean Wstring::equaln(wchar_t * str, unsigned length) { return (Boolean)IS_WEQUALN(string.buffer.start, str, length); } Boolean Wstring::equaln(Wstring * str, unsigned length) { return (Boolean)IS_WEQUALN(string.buffer.start, str->string.buffer.start, length); } Boolean Wstring::equal(wchar_t * str, unsigned off, unsigned length) { return (Boolean)IS_WEQUALN(string.buffer.start + off, str, length); } Boolean Wstring::equal(wchar_t * str, unsigned off) { return (Boolean)IS_WEQUAL(string.buffer.start + off, str); } Boolean Wstring::equal(wchar_t * str) { return equal(str, 0); } Boolean Wstring::equal(Wstring * str, unsigned off, unsigned length) { return (Boolean)IS_WEQUALN(string.buffer.start + off, str->string.buffer.start, length); } Boolean Wstring::equal(Wstring * str) { return equal(str, 0); } Boolean Wstring::equal(Wstring * str, unsigned off) { return (Boolean)IS_WEQUAL(string.buffer.start + off, str->string.buffer.start); } void Wstring::append_to_str(struct _String * str, unsigned off, unsigned length) { append_string(string.buffer.start + off, str, length); } Name Name_set::lookup(const char *key) { for (entry *node = root; node != 0;) { int res = strcmp(key, node->name->string_mb); if (res < 0) { node = node->left; } else if (res > 0) { node = node->right; } else { return node->name; } } return 0; } Name Name_set::insert(const char *key, Boolean &found) { Name name = 0; if (root != 0) { for (entry *node = root; name == 0;) { int res = strcmp(key, node->name->string_mb); if (res < 0) { if (node->left != 0) { node = node->left; } else { found = false; name = ALLOC(Name); node->left = new entry(name, node); rebalance(node); } } else if (res > 0) { if (node->right != 0) { node = node->right; } else { found = false; name = ALLOC(Name); node->right = new entry(name, node); rebalance(node); } } else { found = true; name = node->name; } } } else { found = false; name = ALLOC(Name); root = new entry(name, 0); } return name; } void Name_set::insert(Name name) { if (root != 0) { for (entry *node = root;;) { int res = strcmp(name->string_mb, node->name->string_mb); if (res < 0) { if (node->left != 0) { node = node->left; } else { node->left = new entry(name, node); rebalance(node); break; } } else if (res > 0) { if (node->right != 0) { node = node->right; } else { node->right = new entry(name, node); rebalance(node); break; } } else { // should be an error: inserting already existing name break; } } } else { root = new entry(name, 0); } } void Name_set::rebalance(Name_set::entry *node) { for (; node != 0; node = node->parent) { entry *right = node->right; entry *left = node->left; unsigned rdepth = (right != 0) ? right->depth : 0; unsigned ldepth = (left != 0) ? left->depth : 0; if (ldepth > rdepth + 1) { if ((node->left = left->right) != 0) { left->right->parent = node; } if ((left->parent = node->parent) != 0) { if (node == node->parent->right) { node->parent->right = left; } else { node->parent->left = left; } } else { root = left; } left->right = node; node->parent = left; node->setup_depth(); node = left; } else if (rdepth > ldepth + 1) { if ((node->right = right->left) != 0) { right->left->parent = node; } if ((right->parent = node->parent) != 0) { if (node == node->parent->right) { node->parent->right = right; } else { node->parent->left = right; } } else { root = right; } right->left = node; node->parent = right; node->setup_depth(); node = right; } node->setup_depth(); } } Name_set::iterator Name_set::begin() const { for (entry *node = root; node != 0; node = node->left) { if (node->left == 0) { return iterator(node); } } return iterator(); } Name_set::iterator& Name_set::iterator::operator++() { if (node != 0) { if (node->right != 0) { node = node->right; while (node->left != 0) { node = node->left; } } else { while ((node->parent != 0) && (node->parent->right == node)) { node = node->parent; } node = node->parent; } } return *this; } /* * 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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * mksh.cc * * Execute the command(s) of one Make or DMake rule */ /* * Included files */ #include /* redirect_io() */ #include /* retmem() */ #include #include #include /* * Workaround for NFS bug. Sometimes, when running 'chdir' on a remote * dmake server, it fails with "Stale NFS file handle" error. * The second attempt seems to work. */ int my_chdir(char * dir) { int res = chdir(dir); if (res != 0 && (errno == ESTALE || errno == EAGAIN)) { /* Stale NFS file handle. Try again */ res = chdir(dir); } return res; } /* * File table of contents */ static void change_sunpro_dependencies_value(char *oldpath, char *newpath); static void init_mksh_globals(char *shell); static void set_env_vars(char *env_list[]); static void set_env_vars(char *env_list[]) { char **env_list_p; for (env_list_p = env_list; *env_list_p != (char *) NULL; env_list_p++) { putenv(*env_list_p); } } static void init_mksh_globals(char *shell) { /* MBSTOWCS(wcs_buffer, "SHELL"); shell_name = GETNAME(wcs_buffer, FIND_LENGTH); MBSTOWCS(wcs_buffer, shell); (void) SETVAR(shell_name, GETNAME(wcs_buffer, FIND_LENGTH), false); */ char * dmake_shell; if ((dmake_shell = getenv("DMAKE_SHELL")) == NULL) { dmake_shell = shell; } MBSTOWCS(wcs_buffer, dmake_shell); shell_name = GETNAME(wcs_buffer, FIND_LENGTH); } /* * Change the pathname in the value of the SUNPRO_DEPENDENCIES env variable * from oldpath to newpath. */ static void change_sunpro_dependencies_value(char *oldpath, char *newpath) { char buf[MAXPATHLEN]; static char *env; int length; int oldpathlen; char *sp_dep_value; /* check if SUNPRO_DEPENDENCIES is set in the environment */ if ((sp_dep_value = getenv("SUNPRO_DEPENDENCIES")) != NULL) { oldpathlen = strlen(oldpath); /* check if oldpath is indeed in the value of SUNPRO_DEPENDENCIES */ if (strncmp(oldpath, sp_dep_value, oldpathlen) == 0) { (void) sprintf(buf, "%s%s", newpath, sp_dep_value + oldpathlen); length = 2 + strlen("SUNPRO_DEPENDENCIES") + strlen(buf); env = getmem(length); (void) sprintf(env, "%s=%s", "SUNPRO_DEPENDENCIES", buf); (void) putenv(env); } } } /* * 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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * read.c * * This file contains the makefile reader. */ /* * Included files */ #include /* retmem() */ #include #include /* read() */ #include /* close(), unlink(), read() */ #include #define STRING_LEN_TO_CONVERT (8*1024) /* * get_next_block_fn(source) * * Will get the next block of text to read either * by popping one source bVSIZEOFlock of the stack of Sources * or by reading some more from the makefile. * * Return value: * The new source block to read from * * Parameters: * source The old source block * * Global variables used: * file_being_read The name of the current file, error msg */ Boolean make_state_locked; Source get_next_block_fn(Source source) { off_t to_read; int length; size_t num_wc_chars; char ch_save; char *ptr; if (source == NULL) { return NULL; } if ((source->fd < 0) || ((source->bytes_left_in_file <= 0) && (source->inp_buf_ptr >= source->inp_buf_end))) { /* We can't read from the makefile, so pop the source block */ if (source->fd > 2) { (void) close(source->fd); if (make_state_lockfile != NULL) { (void) unlink(make_state_lockfile); retmem_mb(make_state_lockfile); make_state_lockfile = NULL; make_state_locked = false; } } if (source->string.free_after_use && (source->string.buffer.start != NULL)) { retmem(source->string.buffer.start); source->string.buffer.start = NULL; } if (source->inp_buf != NULL) { retmem_mb(source->inp_buf); source->inp_buf = NULL; } source = source->previous; if (source != NULL) { source->error_converting = false; } return source; } if (source->bytes_left_in_file > 0) { /* * Read the whole makefile. * Hopefully the kernel managed to prefetch the stuff. */ to_read = source->bytes_left_in_file; source->inp_buf_ptr = source->inp_buf = getmem(to_read + 1); source->inp_buf_end = source->inp_buf + to_read; length = read(source->fd, source->inp_buf, (unsigned int) to_read); if (length != to_read) { WCSTOMBS(mbs_buffer, file_being_read); if (length == 0) { fatal_mksh(gettext("Error reading `%s': Premature EOF"), mbs_buffer); } else { fatal_mksh(gettext("Error reading `%s': %s"), mbs_buffer, errmsg(errno)); } } *source->inp_buf_end = nul_char; source->bytes_left_in_file = 0; } /* * Try to convert the next piece. */ ptr = source->inp_buf_ptr + STRING_LEN_TO_CONVERT; if (ptr > source->inp_buf_end) { ptr = source->inp_buf_end; } for (num_wc_chars = 0; ptr > source->inp_buf_ptr; ptr--) { ch_save = *ptr; *ptr = nul_char; num_wc_chars = mbstowcs(source->string.text.end, source->inp_buf_ptr, STRING_LEN_TO_CONVERT); *ptr = ch_save; if (num_wc_chars != (size_t)-1) { break; } } if ((int) num_wc_chars == (size_t)-1) { source->error_converting = true; return source; } source->error_converting = false; source->inp_buf_ptr = ptr; source->string.text.end += num_wc_chars; *source->string.text.end = 0; if (source->inp_buf_ptr >= source->inp_buf_end) { if (*(source->string.text.end - 1) != (int) newline_char) { WCSTOMBS(mbs_buffer, file_being_read); warning_mksh(gettext("newline is not last character in file %s"), mbs_buffer); *source->string.text.end++ = (int) newline_char; *source->string.text.end = (int) nul_char; *source->string.buffer.end++; } if (source->inp_buf != NULL) { retmem_mb(source->inp_buf); source->inp_buf = NULL; } } return source; }