/* * Advanced Linux Sound Architecture part of envy24control * handle profiles for envy24control using alsactl * * Copyright (c) by Dirk Kalis * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #define __PROFILES_C__ #include "envy24control.h" #undef __PROFILES_C__ /* include string search function */ #include "strstr_icase_blank.c" /* include forking process function */ #include "new_process.c" /* replace tilde with home directory */ static char filename_without_tilde[MAX_FILE_NAME_LENGTH]; static char profile_name[PROFILE_NAME_FIELD_LENGTH]; /* * check the environment and use the set variables */ char *check_environment(const char * const env_var, char * const var) { if ( (char *)getenv(env_var) == NULL ) return var; return (char *)getenv(env_var); } /* replace tilde with home directory by checking HOME environment variable */ void subst_tilde_in_filename(char * const filename) { char new_filename[MAX_FILE_NAME_LENGTH]; char *pos_after_tilde; if ((pos_after_tilde = strchr(filename, '~')) != NULL) { pos_after_tilde++; strncpy(new_filename, getenv("HOME"), MAX_FILE_NAME_LENGTH); strncpy(new_filename + strlen(new_filename), pos_after_tilde, MAX_FILE_NAME_LENGTH - strlen(new_filename)); new_filename[MAX_FILE_NAME_LENGTH - 1] = '\0'; strncpy(filename, new_filename, MAX_FILE_NAME_LENGTH); } } int which_cfgfile(char ** const cfgfile) { int res; FILE *inputFile; strncpy(filename_without_tilde, *cfgfile, MAX_FILE_NAME_LENGTH); filename_without_tilde[MAX_FILE_NAME_LENGTH - 1] = '\0'; subst_tilde_in_filename(filename_without_tilde); if ((inputFile = fopen(filename_without_tilde, "r")) == NULL) { if (strcmp(*cfgfile, DEFAULT_PROFILERC) && strcmp(*cfgfile, SYS_PROFILERC)) { res = -ENOENT; } else if (!strcmp(*cfgfile, DEFAULT_PROFILERC) && (inputFile = fopen(SYS_PROFILERC, "r")) == NULL) { res = -ENOENT; } else { fclose(inputFile); *cfgfile = SYS_PROFILERC; res = EXIT_SUCCESS; } } else { fclose(inputFile); *cfgfile = filename_without_tilde; res = EXIT_SUCCESS; } return res; } int get_file_size(const char * const filename) { struct stat file_status; strncpy(filename_without_tilde, filename, MAX_FILE_NAME_LENGTH); filename_without_tilde[MAX_FILE_NAME_LENGTH - 1] = '\0'; subst_tilde_in_filename(filename_without_tilde); if (stat(filename_without_tilde, &file_status) < 0) { fprintf(stderr, "Cannot find file '%s'.\n", filename); return -errno; } return (int)file_status.st_size; } int read_profiles_in_buffer(const char * const cfgfile, void * const buffer, const size_t max_length) { int res; int inputFile; if ((inputFile = open(cfgfile, O_RDONLY)) < 0) { fprintf(stderr, "warning: can't open profiles file '%s' for reading.\n", cfgfile); return -errno; } res = read(inputFile, buffer, max_length); close(inputFile); *(char *)(buffer + max_length - 1) = '\0'; if (res == max_length) { fprintf(stderr, "warning: profiles file '%s' has maximum size reached.\n", cfgfile); fprintf(stderr, "\tThe defined value for MAX_PROFILE_SIZE must be increased in 'profiles.h'.\n"); fprintf(stderr, "\tThen you must recompile the '%s' tool.\n", PROGRAM_NAME); fprintf(stderr, "\tThe current value is %d.\n", MAX_PROFILE_SIZE); } return res; } /* * write buffer with max_length to cfgfile * with introduction if with_intro != 0 */ int write_profiles_from_buffer(const char * const cfgfile, const void * const buffer, const size_t max_length, const int with_intro) { int res, length; time_t date_time[MAX_NUM_STR_LENGTH]; char introduction_part[MAX_SEARCH_FIELD_LENGTH]; int outputFile; if ((outputFile = open(cfgfile, O_WRONLY | O_CREAT | O_TRUNC | 0400000 /* O_NOFOLLOW */, FILE_CREA_MODE)) < 0) { fprintf(stderr, "warning: can't open profiles file '%s' for writing.\n", cfgfile); return -errno; } length = max_length > strlen(buffer) ? strlen(buffer) : max_length; time(date_time); snprintf(introduction_part, MAX_SEARCH_FIELD_LENGTH, "%s'%s'%s%s%s'%s'%s%s%s%s%s%s%s%s%s\n", \ "#\n" \ "# This file is automatically generated by ", PROGRAM_NAME, " at ", \ (char *) asctime(localtime(date_time)), \ "# Do not edit this file manually. This file will be permanently overwritten.\n" \ "# Use ", PROGRAM_NAME, " to modify and store settings.\n" \ "#\n" \ "# File-Structure:\n" \ "# ", PROFILE_HEADER_TEMPL, "\t\t- profile header\n" \ "#\t", CARD_HEADER_TEMPL, "\t- card header\n" \ "#\t", PROFILE_NAME_TEMPL, "\t\t- profile name - optional\n" \ "#\t\t***********************************\n" \ "#\t\t***** ALSA code for this card *****\n" \ "#\t\t***********************************\n" \ "#\t", CARD_FOOTER_TEMPL, "\t- card footer\n" \ "#\n"); introduction_part[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; if (with_intro) res = write(outputFile, introduction_part, strlen(introduction_part)); res = write(outputFile, buffer, length); close(outputFile); if (res < 0) { fprintf(stderr, "warning: can't write profiles file '%s'.\n", cfgfile); } return res; } int create_dir_from_filename(const char * const filename) { int res; char pathname[MAX_FILE_NAME_LENGTH]; char * parameter[MAX_PARAM]; char *mkdir; mkdir = check_environment("MKDIR_PROG", MKDIR); strncpy(pathname, filename, MAX_FILE_NAME_LENGTH); pathname[MAX_FILE_NAME_LENGTH - 1] = '\0'; *strrchr(pathname, '/') = '\0'; subst_tilde_in_filename(pathname); parameter[0] = mkdir; parameter[1] = "-p"; parameter[2] = "-m"; parameter[3] = DIR_CREA_MODE; parameter[4] = pathname; parameter[5] = NULL; res = new_process(parameter); return res; } int compose_search_string(char * const search_string, const char * const templ, const char * const value, const char place_holder, const int max_length) { int res; char *pos_place_holder; memset(search_string, '\0', max_length); strncpy(search_string, templ, max_length); search_string[max_length - 1] = '\0'; if ((pos_place_holder = strchr(search_string, place_holder)) == NULL) { res = NOTFOUND; return res; } strncpy(pos_place_holder, value, max_length - strlen(search_string)); strncpy(pos_place_holder + strlen(value), \ strchr(templ, place_holder) \ + sizeof(char), \ max_length - strlen(search_string)); search_string[max_length - 1] = '\0'; res = EXIT_SUCCESS; return res; } /* search backwards first newline - start of line is position after newline */ int get_start_of_line(const char * const buffer, const int offset) { int begin_of_line; for (begin_of_line = offset; begin_of_line >= 0; begin_of_line--) { if (buffer[begin_of_line] == '\n') break; } begin_of_line++; return begin_of_line; } /* * search tokens sequential for the furthest token backwards from section_end * tokens should be specified from nearest token to the furthest */ int get_begin_of_section(const char * const buffer, char * const section_tokens, const char * const token_sep, const int section_end) { char *search_token; int pos_start_of_section, pos_end_of_section, pos_end_of_search; search_token = strtok(section_tokens, token_sep); pos_end_of_search = section_end; do { pos_start_of_section = 0; pos_end_of_section = 0; while ((pos_start_of_section = strstr_icase_blank(buffer + pos_end_of_section, search_token)) < pos_end_of_search - pos_end_of_section) { if (pos_start_of_section < 0) break; pos_end_of_section += pos_start_of_section; pos_end_of_section++; } pos_end_of_section--; pos_start_of_section = pos_end_of_section; pos_end_of_search = pos_end_of_section; if (pos_start_of_section < 0) { pos_start_of_section = NOTFOUND; // fprintf(stderr, "Begin of section for '%s' can't be found\n", search_token); return pos_start_of_section; } } while ((search_token = strtok(NULL, token_sep)) != NULL); return pos_start_of_section; } /* * search tokens sequential for the nearest token from section_begin * tokens should be specified from furthest token to the nearest */ int get_end_of_section(const char * const buffer, char * const section_tokens, const char * const token_sep, const int section_begin) { char *search_token; int pos_end_of_section, pos_end_of_search; search_token = strtok(section_tokens, token_sep); pos_end_of_section = strlen(buffer); do { pos_end_of_search = strstr_icase_blank(buffer + section_begin, search_token); if (!((pos_end_of_search < 0) || (pos_end_of_search > pos_end_of_section))) { pos_end_of_section = pos_end_of_search; } } while ((search_token = strtok(NULL, token_sep)) != NULL); return pos_end_of_section == strlen(buffer) ? pos_end_of_section : pos_end_of_section + section_begin; } int get_profile_begin(const char * const buffer, const int profile_number) { char header[MAX_SEARCH_FIELD_LENGTH], profile_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char place_holder; /* compose profile header */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", profile_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, PROFILE_HEADER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; return strstr_icase_blank(buffer, header); } int get_profile_end(const char * const buffer, const int profile_number) { char header[MAX_SEARCH_FIELD_LENGTH]; char place_holder; int pos_profile_begin; if ((pos_profile_begin = get_profile_begin(buffer, profile_number)) < 0) return pos_profile_begin; place_holder = PLACE_HOLDER_NUM; strncpy(header, PROFILE_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; *strchr(header, place_holder) = '\0'; return get_end_of_section(buffer, header, TOKEN_SEP, pos_profile_begin + sizeof(char)); } int get_card_begin(const char * const buffer, const int profile_number, const int card_number) { char header[MAX_SEARCH_FIELD_LENGTH], profile_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char place_holder; int pos_profile_begin, pos_profile_end, pos_card_begin; if ((pos_profile_begin = get_profile_begin(buffer, profile_number)) < 0) return pos_profile_begin; pos_profile_end = get_profile_end(buffer, profile_number); /* compose card header */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, CARD_HEADER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; if ((pos_card_begin = strstr_icase_blank(buffer + pos_profile_begin, header)) < 0) return pos_card_begin; if ((pos_card_begin += pos_profile_begin) > pos_profile_end) return NOTFOUND; return pos_card_begin; } int get_card_end(const char * const buffer, const int profile_number, const int card_number) { char header[MAX_SEARCH_FIELD_LENGTH], profile_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char place_holder; int pos_card_begin; if ((pos_card_begin = get_card_begin(buffer, profile_number, card_number)) < 0) return pos_card_begin; /* searching "[ profile | < card | < /card # >" */ /* add "[ profile |" to search_field */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; strncpy(header, PROFILE_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH); *strchr(header, place_holder) = '\0'; strncpy(header + strlen(header), TOKEN_SEP, MAX_SEARCH_FIELD_LENGTH - strlen(header)); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; /* add "< card |" to header */ strncpy(header + strlen(header), CARD_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH - strlen(header)); *strchr(header, place_holder) = '\0'; strncpy(header + strlen(header), TOKEN_SEP, MAX_SEARCH_FIELD_LENGTH - strlen(header)); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; /* add "< /card # >" to header */ compose_search_string(header + strlen(header), CARD_FOOTER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH - strlen(header)); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; return get_end_of_section(buffer, header, TOKEN_SEP, pos_card_begin + sizeof(char)); } int get_pos_for_next_card(const char * const buffer, const int profile_number, const int card_number) { char header[MAX_SEARCH_FIELD_LENGTH], profile_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char place_holder; int pos_next_card; if ((pos_next_card = get_card_end(buffer, profile_number, card_number)) < 0) return pos_next_card; /* add "< /card # >" to header */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, CARD_FOOTER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; if (strstr_icase_blank(buffer + pos_next_card, header) == 0) { while((buffer[pos_next_card] != '\n') && (buffer[pos_next_card] != '\0')) pos_next_card++; if (buffer[pos_next_card] == '\n') pos_next_card++; } return pos_next_card; } int get_number_from_header(const char * const header_string) { char string[MAX_SEARCH_FIELD_LENGTH]; char number_string[MAX_NUM_STR_LENGTH]; char *pos_number; strncpy(string, header_string, MAX_SEARCH_FIELD_LENGTH); string[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; if ((pos_number = strpbrk(string, "0123456789")) == NULL) { return -EINVAL; } else { strncpy(number_string, pos_number, MAX_NUM_STR_LENGTH); number_string[MAX_NUM_STR_LENGTH - 1] = '\0'; } return atoi(number_string); } char *get_profile_name_from_header(const char * const header_string) { char string[MAX_SEARCH_FIELD_LENGTH]; char header_templ[MAX_SEARCH_FIELD_LENGTH]; char left_name_border; memset(profile_name, '\0', PROFILE_NAME_FIELD_LENGTH); strncpy(string, header_string, MAX_SEARCH_FIELD_LENGTH); string[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; strncpy(header_templ, PROFILE_NAME_TEMPL, MAX_SEARCH_FIELD_LENGTH); header_templ[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; string[strstr_icase_blank(string, strchr(header_templ, PLACE_HOLDER_STR) + sizeof(char))] = '\0'; left_name_border = *(strchr(header_templ, PLACE_HOLDER_STR) - sizeof(char)); strncpy(profile_name, strchr(string + sizeof(char), left_name_border) + sizeof(char), PROFILE_NAME_FIELD_LENGTH); profile_name[PROFILE_NAME_FIELD_LENGTH - 1] = '\0'; return profile_name; } /* search max card number in profile */ int get_max_card_number_in_profile(const char * const buffer, const int profile_number) { char header[MAX_SEARCH_FIELD_LENGTH]; char place_holder; int pos_card_next, pos_profile_end, pos_card_begin, card_number, card_number_max; place_holder = PLACE_HOLDER_NUM; card_number_max = NOTFOUND; strncpy(header, CARD_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; *strchr(header, place_holder) = '\0'; pos_card_next = get_profile_begin(buffer, profile_number); pos_profile_end = get_profile_end(buffer, profile_number); while ((pos_card_begin = strstr_icase_blank(buffer + pos_card_next, header)) >= 0) { if ((pos_card_begin += pos_card_next) > pos_profile_end) break; pos_card_next = pos_card_begin + sizeof(char); card_number = get_number_from_header(buffer + pos_card_begin); if (card_number > card_number_max) card_number_max = card_number; } return card_number_max; } int get_pos_name_header_from_card(const char * const buffer, const int profile_number, const int card_number) { char header[MAX_SEARCH_FIELD_LENGTH]; char place_holder; int pos_card_begin, pos_card_end, pos_name_header; pos_card_begin = get_card_begin(buffer, profile_number, card_number); pos_card_end = get_card_end(buffer, profile_number, card_number); place_holder = PLACE_HOLDER_STR; strncpy(header, PROFILE_NAME_TEMPL, MAX_SEARCH_FIELD_LENGTH); *strchr(header, place_holder) = '\0'; header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; if ((pos_name_header = strstr_icase_blank(buffer + pos_card_begin, header)) >= 0) { if ((pos_name_header += pos_card_begin) < pos_card_end) { return pos_name_header; } } return NOTFOUND; } int get_begin_of_alsa_section(const char * const buffer, const int profile_number, const int card_number) { char search_string[MAX_SEARCH_FIELD_LENGTH]; int card_section_begin, card_section_end; int begin_of_alsa_section, pos_after_alsa_section; if ((card_section_begin = get_card_begin(buffer, profile_number, card_number)) < 0) return card_section_begin; card_section_end = get_card_end(buffer, profile_number, card_number); strncpy(search_string, PROFILE_NAME_TEMPL, MAX_SEARCH_FIELD_LENGTH); search_string[MAX_SEARCH_FIELD_LENGTH -1] = '\0'; *strchr(search_string, PLACE_HOLDER_STR) = '\0'; pos_after_alsa_section = get_start_of_line(buffer, card_section_end); begin_of_alsa_section = strstr_icase_blank(buffer + card_section_begin, search_string); begin_of_alsa_section = begin_of_alsa_section < 0 ? card_section_begin : begin_of_alsa_section + card_section_begin; if (begin_of_alsa_section > pos_after_alsa_section) begin_of_alsa_section = card_section_begin; while (begin_of_alsa_section < pos_after_alsa_section) { if (buffer[begin_of_alsa_section] == '\n') { begin_of_alsa_section++; break; } begin_of_alsa_section++; } if (begin_of_alsa_section >= pos_after_alsa_section) begin_of_alsa_section = NOTFOUND; return begin_of_alsa_section; } int reorganize_profiles(char * const buffer, const int max_length) { int profile_number, card_number, card_number_max; int res; int pos_profile_begin, pos_profile_end, pos_card_begin, pos_card_end, pos_name_header; int pos_alsa_section_begin, pos_after_alsa_section; char header[MAX_SEARCH_FIELD_LENGTH]; void *buffer_copy = NULL; char profile_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char place_holder; if ((buffer_copy = malloc(max_length)) == NULL) { res = -ENOBUFS; profile_number = res; fprintf(stderr, "Cannot allocate memory for reorganize profiles.\n"); return res; } memset(buffer_copy, '\0', max_length); for (profile_number = 1; profile_number <= MAX_PROFILES; profile_number++) { if ((pos_profile_begin = get_profile_begin(buffer, profile_number)) < 0) continue; /* write profile header */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", profile_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, PROFILE_HEADER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer_copy + strlen(buffer_copy), max_length - strlen(buffer_copy), "%s\n", header); pos_profile_end = get_profile_end(buffer, profile_number); /* search max card number in profile */ card_number_max = get_max_card_number_in_profile(buffer, profile_number); for (card_number = 0; card_number <= card_number_max; card_number++) { if ((pos_card_begin = get_card_begin(buffer, profile_number, card_number)) < 0) continue; /* write card header */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, CARD_HEADER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer_copy + strlen(buffer_copy), max_length - strlen(buffer_copy), "%s\n", header); pos_card_end = get_card_end(buffer, profile_number, card_number); /* write profile name */ place_holder = PLACE_HOLDER_STR; if ((pos_name_header = get_pos_name_header_from_card(buffer, profile_number, card_number)) >= 0) { compose_search_string(header, PROFILE_NAME_TEMPL, get_profile_name_from_header(buffer + pos_name_header), place_holder, \ MAX_SEARCH_FIELD_LENGTH); snprintf(buffer_copy + strlen(buffer_copy), max_length - strlen(buffer_copy), "%s\n", header); } /* copy alsa section if exists */ if ((pos_alsa_section_begin = get_begin_of_alsa_section(buffer, profile_number, card_number)) >= 0) { pos_after_alsa_section = get_start_of_line(buffer, pos_card_end); strncpy(buffer_copy + strlen(buffer_copy), buffer + pos_alsa_section_begin, pos_after_alsa_section - pos_alsa_section_begin); } /* write card footer */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, CARD_FOOTER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer_copy + strlen(buffer_copy), max_length - strlen(buffer_copy), "%s\n", header); } } memset(buffer, '\0', max_length); strncpy(buffer, buffer_copy, max_length); buffer[max_length - 1] = '\0'; free(buffer_copy); buffer_copy = NULL; return EXIT_SUCCESS; } int delete_card_from_profile(char * const buffer, const int profile_number, const int card_number, const int max_length) { int pos_card_begin, pos_next_card; char *buffer_copy = NULL; if ((pos_card_begin = get_card_begin(buffer, profile_number, card_number)) < 0) return pos_card_begin; if ((buffer_copy = malloc(max_length)) == NULL) { fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); fprintf(stderr, "Cannot delete card '%d' from profile '%d'.\n", card_number, profile_number); return -ENOBUFS; } memset(buffer_copy, '\0', max_length); pos_next_card = get_pos_for_next_card(buffer, profile_number, card_number); strncpy(buffer_copy, buffer, pos_card_begin); strncpy(buffer_copy + pos_card_begin, buffer + pos_next_card, max_length - pos_card_begin); buffer_copy[max_length - 1] = '\0'; memset(buffer, '\0', max_length); strncpy(buffer, buffer_copy, max_length); buffer[max_length - 1] = '\0'; free(buffer_copy); buffer_copy = NULL; return EXIT_SUCCESS; } int save_restore_alsactl_settings(char * const tmpfile, const int card_number, char * const operation) { int res; char * parameter[MAX_PARAM]; char *alsactl; char profile_number_or_card_number_as_str[MAX_NUM_STR_LENGTH]; alsactl = check_environment("ALSACTL_PROG", ALSACTL); snprintf(profile_number_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_number_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; parameter[0] = alsactl; parameter[1] = "-f"; parameter[2] = tmpfile; parameter[3] = operation; parameter[4] = profile_number_or_card_number_as_str; parameter[5] = NULL; res = new_process(parameter); return res; } void compose_tmpfile_name(char * const tmpfile, const char * const cfgfile) { strncpy(tmpfile, cfgfile, MAX_FILE_NAME_LENGTH); tmpfile[MAX_FILE_NAME_LENGTH - 1] = '\0'; strncpy(tmpfile + strlen(tmpfile), "_alsactl_tmp", MAX_FILE_NAME_LENGTH - strlen(tmpfile)); tmpfile[MAX_FILE_NAME_LENGTH - 1] = '\0'; } /* * restore card settings * if profile_number < 0 profile_name must be given * if booth is given profile_number will be used profile_name will be ignored */ int restore_profile(const int profile_number, const int card_number, const char * profile_name, char * cfgfile) { int res, max_length; int begin_of_alsa_section, pos_after_alsa_section, profile_nr; char *buffer = NULL; char tmpfile[MAX_FILE_NAME_LENGTH]; int get_profile_number(const char * const profile_name_given, const int card_number, char * cfgfile); if ((profile_number < 0) && (profile_name == NULL)) { fprintf(stderr, "Without profile number - profile name for card '%d' must given.\n", card_number); return -EINVAL; } if ((max_length = get_file_size(cfgfile)) < 0) { fprintf(stderr, "Cannot get file size from '%s'.\n", cfgfile); return max_length; } max_length++; if ((buffer = malloc(max_length)) == NULL) { fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); fprintf(stderr, "Cannot read settings for card '%d' in profile '%d'.\n", card_number, profile_number); return -ENOBUFS; } memset(buffer, '\0', max_length); if ((res = read_profiles_in_buffer(cfgfile, buffer, max_length)) <= 0) { if (profile_number < 0) { fprintf(stderr, "Cannot read settings for card '%d' in profile '%s'.\n", card_number, profile_name); } else { fprintf(stderr, "Cannot read settings for card '%d' in profile '%d'.\n", card_number, profile_number); } free(buffer); buffer = NULL; return -EINVAL; } profile_nr = profile_number; if (profile_number < 0) { if ((profile_nr = get_profile_number(profile_name, card_number, cfgfile)) < 0) { fprintf(stderr, "Cannot find profile '%s' for card '%d'.\n", profile_name, card_number); free(buffer); buffer = NULL; return profile_nr; } } if ((begin_of_alsa_section = get_begin_of_alsa_section(buffer, profile_nr, card_number)) < 0) { fprintf(stderr, "Cannot find alsa section for card '%d' in profile '%d'.\n", card_number, profile_nr); free(buffer); buffer = NULL; return begin_of_alsa_section; } pos_after_alsa_section = get_start_of_line(buffer, get_card_end(buffer, profile_nr, card_number)); compose_tmpfile_name(tmpfile, cfgfile); if ((res = write_profiles_from_buffer(tmpfile, buffer + begin_of_alsa_section, pos_after_alsa_section - begin_of_alsa_section, 0)) >= 0) { res = save_restore_alsactl_settings(tmpfile, card_number, ALSACTL_OP_RESTORE); unlink(tmpfile); } free(buffer); buffer = NULL; if (res > 0) res = EXIT_SUCCESS; return res; } int append_alsactl_settings(const char * const tmpfile, char * const buffer_position, const int max_length) { int res; res = read_profiles_in_buffer(tmpfile, buffer_position, max_length); if (res >= 0) return EXIT_SUCCESS; return res; } /* * insert entry for card in profile with alsactl settings * if profile_number < 0 no profile header is needed * if pos_end < 0 the new entry will be appended * if profile_name == NULL the profile name header will not be written */ int insert_card(char * const buffer, const int profile_number, const int card_number, const char * const profile_name, const int pos_begin, \ const int pos_end, char * const tmpfile, const int max_length) { int res; char *buffer_copy = NULL; char header[MAX_SEARCH_FIELD_LENGTH]; char profile_number_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char profile_name_copy[PROFILE_NAME_FIELD_LENGTH]; char place_holder; if ((res = save_restore_alsactl_settings(tmpfile, card_number, ALSACTL_OP_STORE)) < 0) return res; if (pos_end >= 0) { if ((buffer_copy = malloc(max_length)) == NULL) { fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); fprintf(stderr, "Cannot save settings for card '%d' in profile '%d'.\n", card_number, profile_number); unlink(tmpfile); return -ENOBUFS; } memset(buffer_copy, '\0', max_length); strncpy(buffer_copy, buffer, max_length); buffer_copy[max_length - 1] = '\0'; memset(buffer + pos_begin, '\0', max_length - pos_begin); } if (profile_number > 0) { place_holder = PLACE_HOLDER_NUM; snprintf(profile_number_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", profile_number); profile_number_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, PROFILE_HEADER_TEMPL, profile_number_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer + strlen(buffer), max_length - strlen(buffer), "%s\n", header); buffer[max_length - 1] = '\0'; } /* compose card header */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_number_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_number_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, CARD_HEADER_TEMPL, profile_number_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer + strlen(buffer), max_length - strlen(buffer), "%s\n", header); buffer[max_length - 1] = '\0'; /* compose profile name header if needed */ if (profile_name != NULL) { strncpy(profile_name_copy, profile_name, PROFILE_NAME_FIELD_LENGTH); profile_name_copy[PROFILE_NAME_FIELD_LENGTH - 1] = '\0'; place_holder = PLACE_HOLDER_STR; compose_search_string(header, PROFILE_NAME_TEMPL, profile_name_copy, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer + strlen(buffer), max_length - strlen(buffer), "%s\n", header); buffer[max_length - 1] = '\0'; } res = append_alsactl_settings(tmpfile, buffer + strlen(buffer), max_length - strlen(buffer)); buffer[max_length - 1] = '\0'; unlink(tmpfile); /* compose card footer */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_number_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_number_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(header, CARD_FOOTER_TEMPL, profile_number_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; snprintf(buffer + strlen(buffer), max_length - strlen(buffer), "%s\n", header); buffer[max_length - 1] = '\0'; if (pos_end >= 0) { strncpy(buffer + strlen(buffer), buffer_copy + pos_end, max_length - strlen(buffer)); free(buffer_copy); buffer_copy = NULL; } if (res >= 0) res = EXIT_SUCCESS; return res; } int save_profile(const int profile_number, const int card_number, const char * const profile_name, char *cfgfile) { int res, profile_begin, profile_end, profile_nr; int card_begin, pos_next_card, card_nr, card_number_max; const int no_profile_header = -1; const int append = -1; char *buffer = NULL; char tmpfile[MAX_FILE_NAME_LENGTH]; int max_length; if ((max_length = get_file_size(cfgfile)) < 0) { fprintf(stderr, "This operation will create a new profiles file '%s'.\n", cfgfile); max_length = 0; } max_length += MAX_PROFILE_SIZE; if ((buffer = malloc(max_length)) == NULL) { fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); fprintf(stderr, "Cannot save settings for card '%d' in profile '%d'.\n", card_number, profile_number); return -ENOBUFS; } memset(buffer, '\0', max_length); compose_tmpfile_name(tmpfile, cfgfile); /* file found */ if ((res = open(cfgfile, O_RDONLY | 0400000 /* NOFOLLOW */)) >= 0) { close(res); res = read_profiles_in_buffer(cfgfile, buffer, max_length); if (res > 0) res = reorganize_profiles(buffer, max_length); } res = strlen(buffer); if (res > 0) { if ((profile_begin = get_profile_begin(buffer, profile_number)) < 0) { if (profile_number < MAX_PROFILES) { for (profile_nr = 1; profile_nr <= MAX_PROFILES; profile_nr++) { if (profile_nr > profile_number) { if ((profile_begin = get_profile_begin(buffer, profile_nr)) >= 0) break; } } if (profile_begin < 0) profile_begin = strlen(buffer); } else { profile_begin = strlen(buffer); } if (profile_begin < strlen(buffer)) { res = insert_card(buffer, profile_number, card_number, profile_name, profile_begin, profile_begin, tmpfile, max_length); } else { res = insert_card(buffer, profile_number, card_number, profile_name, profile_begin, append, tmpfile, max_length); } } else { if ((card_begin = get_card_begin(buffer, profile_number, card_number)) < 0) { card_number_max = get_max_card_number_in_profile(buffer, profile_number); profile_end = get_profile_end(buffer, profile_number); if (card_number_max > card_number) { for (card_nr = 0; card_nr <= card_number_max; card_nr++) { if (card_nr > card_number) { if ((card_begin = get_card_begin(buffer, profile_number, card_number)) >= 0) break; } } if (card_begin < 0) card_begin = profile_end; } else { card_begin = profile_end; } if (card_begin < strlen(buffer)) { res = insert_card(buffer, no_profile_header, card_number, profile_name, card_begin, card_begin, tmpfile, max_length); } else { res = insert_card(buffer, no_profile_header, card_number, profile_name, strlen(buffer), append, tmpfile, max_length); } } else { pos_next_card = get_pos_for_next_card(buffer, profile_number, card_number); res = insert_card(buffer, no_profile_header, card_number, profile_name, card_begin, pos_next_card, tmpfile, max_length); } } } else { res = insert_card(buffer, profile_number, card_number, profile_name, 0, -1, tmpfile, max_length); } if (res < 0) { fprintf(stderr, "Cannot store profile '%d' for card '%d'.\n", profile_number, card_number); } else { res = write_profiles_from_buffer(cfgfile, buffer, max_length, 1); } free(buffer); buffer = NULL; if (res > 0) res = EXIT_SUCCESS; return res; } int delete_card(const int card_number, char * cfgfile) { int res, profile_number, max_length; void *buffer = NULL; if (cfgfile == NULL) cfgfile = DEFAULT_PROFILERC; strncpy(filename_without_tilde, cfgfile, MAX_FILE_NAME_LENGTH); filename_without_tilde[MAX_FILE_NAME_LENGTH - 1] = '\0'; subst_tilde_in_filename(filename_without_tilde); cfgfile = filename_without_tilde; if ((res = open(cfgfile, O_RDWR | 0400000 /* O_NOFOLLOW */, FILE_CREA_MODE)) < 0) { fprintf(stderr, "Cannot open configuration file '%s' for writing.\n", cfgfile); fprintf(stderr, "Cannot save settings for card '%d'.\n", card_number); return -errno; } close(res); if (res >= 0) { if ((max_length = get_file_size(cfgfile)) < 0) { fprintf(stderr, "Cannot get file size for '%s'.\n", cfgfile); return max_length; } max_length++; if ((buffer = malloc(max_length)) == NULL) { fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); fprintf(stderr, "Cannot delete card '%d'.\n", card_number); return -ENOBUFS; } memset(buffer, '\0', max_length); res = read_profiles_in_buffer(cfgfile, buffer, max_length); if (res > 0) { for (profile_number = 1; profile_number <= MAX_PROFILES; profile_number++) delete_card_from_profile(buffer, profile_number, card_number, max_length); res = EXIT_SUCCESS; } else { res = NOTFOUND; } res = write_profiles_from_buffer(cfgfile, buffer, max_length, 1); free(buffer); buffer = NULL; } else { res = NOTFOUND; } return res; } /* * First search profile name. if profile name is found look that this profile * name is from the specified card number. * if not search next occurence from given profile name. */ int get_profile_number(const char * const profile_name_given, const int card_number, char * cfgfile) { int res, pos_name, pos_name_offset, pos_begin, pos_end, found; int profile_number, pos_profile; void *buffer = NULL; char search_field[MAX_SEARCH_FIELD_LENGTH]; char header_templ[MAX_SEARCH_FIELD_LENGTH]; char profile_or_card_number_as_str[MAX_NUM_STR_LENGTH]; char place_holder; int max_length; if (strlen(profile_name_given) == 0) { fprintf(stderr, "Profile name for card '%d' must be given.\n", card_number); return -EINVAL; } /* cut profile name to MAX_PROFILE_NAME_LENGTH */ strncpy(profile_name, profile_name_given, PROFILE_NAME_FIELD_LENGTH); profile_name[PROFILE_NAME_FIELD_LENGTH - 1] = '\0'; if (cfgfile == NULL) cfgfile = DEFAULT_PROFILERC; res = which_cfgfile(&cfgfile); if (res < 0) { profile_number = res; } else { if ((max_length = get_file_size(cfgfile)) < 0) { fprintf(stderr, "Cannot get file size from '%s'.\n", cfgfile); return max_length; } max_length++; if ((buffer = malloc(max_length)) == NULL) { res = -ENOBUFS; profile_number = res; fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); return profile_number; } memset(buffer, '\0', max_length); res = read_profiles_in_buffer(cfgfile, buffer, max_length); if (res > 0) { /* insert profile name in PROFILE_NAME_TEMPL */ place_holder = PLACE_HOLDER_STR; compose_search_string(search_field, PROFILE_NAME_TEMPL, profile_name, place_holder, MAX_SEARCH_FIELD_LENGTH); pos_name = 0; pos_name_offset = 0; pos_begin = NOTFOUND; found = 0; while ((pos_name = strstr_icase_blank(buffer + pos_name_offset, search_field)) >= 0) { pos_name += pos_name_offset; /* search begin of section for the given card from profile name pointer backward */ /* insert card number in CARD_HEADER_TEMPL */ place_holder = PLACE_HOLDER_NUM; snprintf(profile_or_card_number_as_str, MAX_NUM_STR_LENGTH, "%d", card_number); profile_or_card_number_as_str[MAX_NUM_STR_LENGTH - 1] = '\0'; compose_search_string(search_field, CARD_HEADER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); search_field[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; if ((pos_begin = get_begin_of_section(buffer, search_field, TOKEN_SEP, pos_name)) < 0) break; /* searching "[ profile | < card | < /card # >" */ /* add "[ profile |" to search_field */ strncpy(header_templ, PROFILE_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH); *strchr(header_templ, place_holder) = '\0'; strncpy(search_field, header_templ, MAX_SEARCH_FIELD_LENGTH); strncpy(search_field + strlen(search_field), TOKEN_SEP, MAX_SEARCH_FIELD_LENGTH - strlen(search_field)); /* add "< card |" to search_field */ strncpy(header_templ, CARD_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH); *strchr(header_templ, place_holder) = '\0'; strncpy(search_field + strlen(search_field), header_templ, MAX_SEARCH_FIELD_LENGTH - strlen(search_field)); strncpy(search_field + strlen(search_field), TOKEN_SEP, MAX_SEARCH_FIELD_LENGTH - strlen(search_field)); search_field[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; /* add "< card # >" to search_field */ compose_search_string(header_templ, CARD_FOOTER_TEMPL, profile_or_card_number_as_str, place_holder, MAX_SEARCH_FIELD_LENGTH); header_templ[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; strncpy(search_field + strlen(search_field), header_templ, MAX_SEARCH_FIELD_LENGTH - strlen(search_field)); search_field[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; pos_end = get_end_of_section(buffer, search_field, TOKEN_SEP, pos_begin + sizeof(char)); if ((pos_name > pos_begin) && (pos_name < pos_end) && (pos_begin >= 0) && (pos_end >= 0)) { found = 1; break; } pos_name_offset = pos_name + sizeof(char); place_holder = PLACE_HOLDER_STR; compose_search_string(search_field, PROFILE_NAME_TEMPL, profile_name, place_holder, MAX_SEARCH_FIELD_LENGTH); } if (found) { place_holder = PLACE_HOLDER_NUM; strncpy(search_field, PROFILE_HEADER_TEMPL, MAX_SEARCH_FIELD_LENGTH); search_field[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; *strchr(search_field, place_holder) = '\0'; if ((pos_profile = get_begin_of_section(buffer, search_field, TOKEN_SEP, pos_begin)) < 0) { profile_number = NOTFOUND; } else { profile_number = get_number_from_header(buffer + pos_profile); /* check profile header syntax */ if (get_profile_begin(buffer, profile_number) != pos_profile) { profile_number = -EINVAL; /* only the profile line */ strncpy(search_field, buffer + pos_profile, MAX_SEARCH_FIELD_LENGTH); search_field[MAX_SEARCH_FIELD_LENGTH - 1] = '\0'; *strchr(search_field, '\n') = '\0'; fprintf(stderr, "profile header '%s' has incorrect syntax.\n", search_field); fprintf(stderr, "profile header syntax is '%s'\n" \ "by replacing place holder '%c' with profile number.\n", \ PROFILE_HEADER_TEMPL, PLACE_HOLDER_NUM); /* check profile number */ } else if (profile_number < 1 || profile_number > MAX_PROFILES) { fprintf(stderr, "profile number '%d' is incorrect. the profile number will be in [1 ... %d].\n", \ profile_number, MAX_PROFILES); profile_number = -EINVAL; } } } else { profile_number = NOTFOUND; } } else { profile_number = NOTFOUND; } free(buffer); buffer = NULL; } return profile_number; } char *get_profile_name(const int profile_number, const int card_number, char * cfgfile) { int res, max_length; void *buffer = NULL; if (profile_number < 1 || profile_number > MAX_PROFILES) { fprintf(stderr, "profile number '%d' is incorrect. the profile number will be in [1 ... %d].\n", \ profile_number, MAX_PROFILES); return NULL; } if (cfgfile == NULL) cfgfile = DEFAULT_PROFILERC; res = which_cfgfile(&cfgfile); if (res < 0) { snprintf(profile_name, PROFILE_NAME_FIELD_LENGTH, "%d", profile_number); } else { if ((max_length = get_file_size(cfgfile)) < 0) { fprintf(stderr, "Cannot get file size from '%s'.\n", cfgfile); snprintf(profile_name, PROFILE_NAME_FIELD_LENGTH, "%d", profile_number); return profile_name; } max_length++; if ((buffer = malloc(max_length)) == NULL) { res = -ENOBUFS; snprintf(profile_name, PROFILE_NAME_FIELD_LENGTH, "%d", profile_number); fprintf(stderr, "Cannot allocate memory for reading profiles.\n"); return profile_name; } memset(buffer, '\0', max_length); memset(profile_name, '\0', PROFILE_NAME_FIELD_LENGTH); res = read_profiles_in_buffer(cfgfile, buffer, max_length); if (res > 0) { if ((res = get_pos_name_header_from_card(buffer, profile_number, card_number)) >= 0) { get_profile_name_from_header(buffer + (res * sizeof(char))); profile_name[PROFILE_NAME_FIELD_LENGTH - 1] = '\0'; } } free(buffer); buffer = NULL; if (strlen(profile_name) == 0) { snprintf(profile_name, PROFILE_NAME_FIELD_LENGTH, "%d", profile_number); } } return profile_name; } int save_restore(const char * const operation, const int profile_number, const int card_number, char * cfgfile, const char * const profile_name) { int res; if (profile_number < 1 || profile_number > MAX_PROFILES) { fprintf(stderr, "profile number '%d' is incorrect. the profile number will be in [1 ... %d].\n", \ profile_number, MAX_PROFILES); return -EINVAL; } if (cfgfile == NULL) cfgfile = DEFAULT_PROFILERC; if (!strcmp(operation, ALSACTL_OP_STORE)) { strncpy(filename_without_tilde, cfgfile, MAX_FILE_NAME_LENGTH); filename_without_tilde[MAX_FILE_NAME_LENGTH - 1] = '\0'; subst_tilde_in_filename(filename_without_tilde); cfgfile = filename_without_tilde; if ((res = open(cfgfile, O_RDONLY | 0400000 /* O_NOFOLLOW */)) < 0) { if ((res = create_dir_from_filename(cfgfile)) < 0) { fprintf(stderr, "Cannot open configuration file '%s' for writing.\n", cfgfile); fprintf(stderr, "Cannot save settings for card '%d' in profile '%d'.\n", card_number, profile_number); return -EACCES; } if ((res = open(cfgfile, O_RDWR | O_CREAT | 0400000 /* O_NOFOLLOW */, FILE_CREA_MODE)) < 0) { fprintf(stderr, "Cannot open configuration file '%s' for writing.\n", cfgfile); fprintf(stderr, "Cannot save settings for card '%d' in profile '%d'.\n", card_number, profile_number); return -errno; } unlink(cfgfile); } else { if ((res = open(cfgfile, O_RDWR | 0400000 /* O_NOFOLLOW */, FILE_CREA_MODE)) < 0) { fprintf(stderr, "Cannot open configuration file '%s' for writing.\n", cfgfile); fprintf(stderr, "Cannot save settings for card '%d' in profile '%d'.\n", card_number, profile_number); return -errno; } } res = save_profile(profile_number, card_number, profile_name, cfgfile); } else if (!strcmp(operation, ALSACTL_OP_RESTORE)) { res = which_cfgfile(&cfgfile); if (res < 0) { fprintf(stderr, "Cannot open profiles file '%s' ...\n", cfgfile); fprintf(stderr, "Use current settings.\n"); fprintf(stderr, "You can store this settings to profile no. %d in file '%s' by pressing save button.\n", profile_number, cfgfile); } else { if ((res = restore_profile(profile_number, card_number, profile_name, cfgfile)) < 0) { fprintf(stderr, "Cannot restore settings for card '%d' in profile '%d'.\n", card_number, profile_number); fprintf(stderr, "Use current settings.\n"); } } } else { fprintf(stderr, "%s: Unknown command '%s'...\n", PROGRAM_NAME, operation); res = -ENODEV; } return res < 0 ? -EXIT_FAILURE : EXIT_SUCCESS; }