+/*
+ * This function appends an option to a list
+ */
+static struct mod_opt_t *append_option( struct mod_opt_t *opt_list, char *opt )
+{
+ struct mod_opt_t *ol = opt_list;
+
+ if( ol ) {
+ while( ol-> m_next ) {
+ ol = ol-> m_next;
+ }
+ ol-> m_next = xmalloc( sizeof( struct mod_opt_t ) );
+ ol = ol-> m_next;
+ } else {
+ ol = opt_list = xmalloc( sizeof( struct mod_opt_t ) );
+ }
+
+ ol-> m_opt_val = bb_xstrdup( opt );
+ ol-> m_next = NULL;
+
+ 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 bb_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 = bb_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 */
+
+/*
+ * 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;
+ char *p;
+
+ p = strchr ( buffer, '#' );
+ if ( p )
+ *p = 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 ) {
+ if (( strncmp ( buffer, "alias", 5 ) == 0 ) && isspace ( buffer [5] )) {
+ 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) = (struct dep_t *) xcalloc ( 1, sizeof ( struct dep_t ));
+ }
+ else {
+ (*current)-> m_next = (struct dep_t *) xcalloc ( 1, sizeof ( struct dep_t ));
+ (*current) = (*current)-> m_next;
+ }
+ (*current)-> m_name = bb_xstrdup ( alias );
+ (*current)-> m_isalias = 1;
+
+ if (( strcmp ( mod, "off" ) == 0 ) || ( strcmp ( mod, "null" ) == 0 )) {
+ (*current)-> m_depcnt = 0;
+ (*current)-> m_deparr = 0;
+ }
+ else {
+ (*current)-> m_depcnt = 1;
+ (*current)-> m_deparr = xmalloc ( 1 * sizeof( char * ));
+ (*current)-> m_deparr[0] = bb_xstrdup ( mod );
+ }
+ (*current)-> m_next = 0;
+ }
+ }
+ else if (( strncmp ( buffer, "options", 7 ) == 0 ) && isspace ( buffer [7] )) {
+ 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 (( strncmp ( buffer, "include", 7 ) == 0 ) && isspace ( buffer [7] )) {
+
+ int fdi; char *filename = buffer + 8;
+
+ while ( isspace ( *filename ))
+ filename++;
+
+ if (( fdi = open ( filename, O_RDONLY )) >= 0 ) {
+ include_conf(first, current, buffer, buflen, fdi);
+ close(fdi);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * This function builds a list of dependency rules from /lib/modules/`uname -r`\modules.dep.
+ * It then fills every modules and aliases with their default options, found by parsing
+ * modprobe.conf (or modules.conf, or conf.modules).
+ */