+
+ ol->m_opt_val = xstrdup(opt);
+ /*ol->m_next = NULL; - done by xzalloc*/
+
+ return opt_list;
+}
+
+#if ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS
+/* static char* parse_command_string(char* src, char **dst);
+ * src: pointer to string containing argument
+ * dst: pointer to where to store the parsed argument
+ * return value: the pointer to the first char after the parsed argument,
+ * NULL if there was no argument parsed (only trailing spaces).
+ * Note that memory is allocated with xstrdup when a new argument was
+ * parsed. Don't forget to free it!
+ */
+#define ARG_EMPTY 0x00
+#define ARG_IN_DQUOTES 0x01
+#define ARG_IN_SQUOTES 0x02
+static char *parse_command_string(char *src, char **dst)
+{
+ int opt_status = ARG_EMPTY;
+ char* tmp_str;
+
+ /* Dumb you, I have nothing to do... */
+ if (src == NULL) return src;
+
+ /* Skip leading spaces */
+ while (*src == ' ') {
+ src++;
+ }
+ /* Is the end of string reached? */
+ if (*src == '\0') {
+ return NULL;
+ }
+ /* Reached the start of an argument
+ * By the way, we duplicate a little too much
+ * here but what is too much is freed later. */
+ *dst = tmp_str = xstrdup(src);
+ /* Get to the end of that argument */
+ while (*tmp_str != '\0'
+ && (*tmp_str != ' ' || (opt_status & (ARG_IN_DQUOTES | ARG_IN_SQUOTES)))
+ ) {
+ switch (*tmp_str) {
+ case '\'':
+ if (opt_status & ARG_IN_DQUOTES) {
+ /* Already in double quotes, keep current char as is */
+ } else {
+ /* shift left 1 char, until end of string: get rid of the opening/closing quotes */
+ memmove(tmp_str, tmp_str + 1, strlen(tmp_str));
+ /* mark me: we enter or leave single quotes */
+ opt_status ^= ARG_IN_SQUOTES;
+ /* Back one char, as we need to re-scan the new char there. */
+ tmp_str--;
+ }
+ break;
+ case '"':
+ if (opt_status & ARG_IN_SQUOTES) {
+ /* Already in single quotes, keep current char as is */
+ } else {
+ /* shift left 1 char, until end of string: get rid of the opening/closing quotes */
+ memmove(tmp_str, tmp_str + 1, strlen(tmp_str));
+ /* mark me: we enter or leave double quotes */
+ opt_status ^= ARG_IN_DQUOTES;
+ /* Back one char, as we need to re-scan the new char there. */
+ tmp_str--;
+ }
+ break;
+ case '\\':
+ if (opt_status & ARG_IN_SQUOTES) {
+ /* Between single quotes: keep as is. */
+ } else {
+ switch (*(tmp_str+1)) {
+ case 'a':
+ case 'b':
+ case 't':
+ case 'n':
+ case 'v':
+ case 'f':
+ case 'r':
+ case '0':
+ /* We escaped a special character. For now, keep
+ * both the back-slash and the following char. */
+ tmp_str++;
+ src++;
+ break;
+ default:
+ /* We escaped a space or a single or double quote,
+ * or a back-slash, or a non-escapable char. Remove
+ * the '\' and keep the new current char as is. */
+ memmove(tmp_str, tmp_str + 1, strlen(tmp_str));
+ break;
+ }
+ }
+ break;
+ /* Any other char that is special shall appear here.
+ * Example: $ starts a variable
+ case '$':
+ do_variable_expansion();
+ break;
+ * */
+ default:
+ /* any other char is kept as is. */
+ break;
+ }
+ tmp_str++; /* Go to next char */
+ src++; /* Go to next char to find the end of the argument. */
+ }
+ /* End of string, but still no ending quote */
+ if (opt_status & (ARG_IN_DQUOTES | ARG_IN_SQUOTES)) {
+ bb_error_msg_and_die("unterminated (single or double) quote in options list: %s", src);
+ }
+ *tmp_str++ = '\0';
+ *dst = xrealloc(*dst, (tmp_str - *dst));
+ return src;
+}
+#else
+#define parse_command_string(src, dst) (0)
+#endif /* ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS */
+
+static int is_conf_command(char *buffer, const char *command)
+{
+ int len = strlen(command);
+ return ((strstr(buffer, command) == buffer) &&
+ isspace(buffer[len]));
+}
+
+/*
+ * This function reads aliases and default module options from a configuration file
+ * (/etc/modprobe.conf syntax). It supports includes (only files, no directories).
+ */
+static void include_conf(struct dep_t **first, struct dep_t **current, char *buffer, int buflen, int fd)
+{
+ int continuation_line = 0;
+
+ // alias parsing is not 100% correct (no correct handling of continuation lines within an alias)!
+
+ while (reads(fd, buffer, buflen)) {
+ int l;
+
+ *strchrnul(buffer, '#') = '\0';
+
+ l = strlen(buffer);
+
+ while (l && isspace(buffer[l-1])) {
+ buffer[l-1] = '\0';
+ l--;
+ }
+
+ if (l == 0) {
+ continuation_line = 0;
+ continue;
+ }
+
+ if (continuation_line)
+ continue;
+
+ if (is_conf_command(buffer, "alias")) {
+ char *alias, *mod;
+
+ if (parse_tag_value(buffer + 6, &alias, &mod)) {
+ /* handle alias as a module dependent on the aliased module */
+ if (!*current) {
+ (*first) = (*current) = xzalloc(sizeof(struct dep_t));
+ } else {
+ (*current)->m_next = xzalloc(sizeof(struct dep_t));
+ (*current) = (*current)->m_next;
+ }
+ (*current)->m_name = xstrdup(alias);
+ (*current)->m_isalias = 1;
+
+ if ((strcmp(mod, "off") == 0) || (strcmp(mod, "null") == 0)) {
+ /*(*current)->m_depcnt = 0; - done by xzalloc */
+ /*(*current)->m_deparr = 0;*/
+ } else {
+ (*current)->m_depcnt = 1;
+ (*current)->m_deparr = xmalloc(sizeof(char *));
+ (*current)->m_deparr[0] = xstrdup(mod);
+ }
+ /*(*current)->m_next = NULL; - done by xzalloc */
+ }
+ } else if (is_conf_command(buffer, "options")) {
+ char *mod, *opt;
+
+ /* split the line in the module/alias name, and options */
+ if (parse_tag_value(buffer + 8, &mod, &opt)) {
+ struct dep_t *dt;
+
+ /* find the corresponding module */
+ for (dt = *first; dt; dt = dt->m_next) {
+ if (strcmp(dt->m_name, mod) == 0)
+ break;
+ }
+ if (dt) {
+ if (ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS) {
+ char* new_opt = NULL;
+ while ((opt = parse_command_string(opt, &new_opt))) {
+ dt->m_options = append_option(dt->m_options, new_opt);
+ }
+ } else {
+ dt->m_options = append_option(dt->m_options, opt);
+ }
+ }
+ }
+ } else if (is_conf_command(buffer, "include")) {
+ int fdi;
+ char *filename;
+
+ filename = skip_whitespace(buffer + 8);
+ fdi = open(filename, O_RDONLY);
+ if (fdi >= 0) {
+ include_conf(first, current, buffer, buflen, fdi);
+ close(fdi);
+ }
+ } else if (ENABLE_FEATURE_MODPROBE_BLACKLIST &&
+ (is_conf_command(buffer, "blacklist"))) {
+ char *mod;
+ struct dep_t *dt;
+
+ mod = skip_whitespace(buffer + 10);
+ for (dt = *first; dt; dt = dt->m_next) {
+ if (strcmp(dt->m_name, mod) == 0)
+ break;
+ }
+ if (dt)
+ dt->m_isblacklisted = 1;
+ }
+ } /* while (reads(...)) */