From d552f0ca3755058fc962359f2d69e13f4f250794 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 16 May 2004 13:37:31 +0000 Subject: [PATCH] Added missing files for profile patch --- envy24control/README.profiles | 101 +++ envy24control/new_process.c | 86 +++ envy24control/profiles.c | 1165 +++++++++++++++++++++++++++++++++ envy24control/profiles.h | 84 +++ 4 files changed, 1436 insertions(+) create mode 100644 envy24control/README.profiles create mode 100644 envy24control/new_process.c create mode 100644 envy24control/profiles.c create mode 100644 envy24control/profiles.h diff --git a/envy24control/README.profiles b/envy24control/README.profiles new file mode 100644 index 0000000..69052f8 --- /dev/null +++ b/envy24control/README.profiles @@ -0,0 +1,101 @@ + Profiles management as part of alsa + 2004-05-16 Dirk Kalis + +General +======= + +Profiles management can be used from all applications like mixers or hardware control programs. +It uses always alsactl and if directorys for the profiles file doesn't exists - mkdir. +profiles file means the file in which the profiles will be stored. +For other application the following files are needed: +profiles.h - header file with the exported functions +profiles.c - profiles implementation +new_process.c - used to start external programs (alsactl and mkdir) +strstr_icase_blank.c - string search function with ignoring case sensitivity, number of blanks, empty and + comment lines (first non blank character '#') +Profile numbers beginning with number 1 not 0 ! + +Introduction +============ + +Profiles management stores and restores alsactl settings in a profiles file. +Every card is stored in the profiles and every card profile can have a profile name. +The profiles file has the following structure: + +[ profile ] +< Card > +{ / > +-----next card or next profile or end of file----- + +The header for profile name and card footer are optional. +The functions in profile.c write always a card footer. + +DO NOT EDIT THIS FILE MANUALLY BECAUSE EVERY WRITE ACCESS MAKE A REORGANIZATION AND COMMENTS NOT WRITTEN IN +THE ALSACTL SECTION WILL BE REMOVED! ALSO THE STRUCTURE OF THE FILE WILL BE MODIFIED! + +With the environment variables ALACTL_PROG and MKDIR_PROG can the compiled defaults for this +programs be overwritten. +e.g.: +export ALSACTL_PROG=;export MKDIR_PROG=; + +This pathes must not be a link ! + +The profiles file name and path can explicit given. +If is not given (by value NULL) two defined defaults will be used. +Two variables can be set in the profiles header file for the default profiles file and for the system profiles file. +The default for DEFAULT_PROFILERC is ~//profiles.conf. +The default for SYS_PROFILERC is /etc//profiles.conf + +The profiles management search for profiles file if no profiles file name is given. +Search order for restore: + if not -> if doesn't exists -> + +Search order for save: + if not -> + +The SYS_PROFILERC can only be written as root by explicit given profiles file name or +you make a copy as root from one of the DEFAULT_PROFILERC. + +This files must not be a link! + +The delete_card function can be used to remove card profiles from profiles file if a card is removed from your pc. + +Miscellaneous cards can have same profile names - thats no problem. +One card with equal profile names is also no problem if you use profile number for searching. +If you use profile name for searching you will allways get the least profile number with this profile name. + +Profiles management in envy24control +==================================== + +In envy24control you can find a new map named "Profiles". +In this map you can store 8 profiles for every envy24 card. +You can change/give name by activating this profile and then click in the name entry. +You can only change the name for the active profile. +If all settings in the active profile are done you can save settings by clicking button "Save active profile". +Error messages will only displayed on stderr in the text window if you start envy24control from xterm. + +To activate a profile click only the profile button. + +You can start envy24control with given profiles file and a default profile. if a default profile is given +after initialisation of envy24control this profile will be activated. +Default profile can be given by profile number or profile name. +The accuracy of discrimination works in the following way: + - if the given default profile contains a letter -> profile name + - if the given default profile has a number outside the range [1 ... MAX_PROFILES] -> profile name + - the given default profile is interpreted as profile number. + +Profile names should have at least one letter. + +For "Delete card from profiles" and equal profile names read above. +One card with equal names is only a problem if you start envy24control with default profile name. You activate +allways the profile with the least profile number with this profile name. + +Copyright +========= + +Copyright Dirk Kalis +This is free and can be distributed under GPL. diff --git a/envy24control/new_process.c b/envy24control/new_process.c new file mode 100644 index 0000000..7ea89a6 --- /dev/null +++ b/envy24control/new_process.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include + +#ifndef MAX_PARAM +#define MAX_PARAM 10 +#endif + +/* + * start child process + */ +int new_process(char * const cmd_line[MAX_PARAM]) +{ + int proc_status; + pid_t pid; + pid_t w; + struct stat file_status; + + /* memory for storage of function pointers from the signal handling routines */ + void (*int_stat)(); + void (*quit_stat)(); + void (*usr2_stat)(); + + /* + * check command file + */ + + /* search file */ + if (stat(cmd_line[0], &file_status) < 0) { + fprintf(stderr, "Cannot find program '%s'.\n", cmd_line[0]); + fprintf(stderr, "You must specify path for '%s'.\n", cmd_line[0]); + return -errno; + } + + proc_status = 0; + /* check file status and permissions */ + if (file_status.st_mode & S_IFREG) { + if (!(file_status.st_mode & S_IXOTH)) { + if (!(file_status.st_mode & S_IXGRP)) { + if (!(file_status.st_mode & S_IXUSR)) { + proc_status = -EACCES; + } else if (file_status.st_uid != getuid()) { + proc_status = -EACCES; + } + } else if ((file_status.st_gid != getgid()) && (file_status.st_uid != getuid())) { + proc_status = -EACCES; + } + } + } else { + proc_status = -EACCES; + } + + if (proc_status != 0) { + fprintf(stderr, "No permissions to execute program '%s'.\n", cmd_line[0]); + return proc_status; + } + + if ( (pid = fork() ) == 0) { + execv(cmd_line[0], cmd_line); + } + + /* for waiting ingnoring special interrupts */ + + int_stat = signal(SIGINT, SIG_IGN); + quit_stat = signal(SIGQUIT, SIG_IGN); + usr2_stat = signal(SIGUSR2, SIG_IGN); + + /* waiting for the end of the child process */ + + while ( ( (w = wait(&proc_status)) != pid ) && (w != -1) ) + ; + if (w == -1) { + proc_status = -errno; + } + + /* restore pointers from signal handling routines */ + + signal(SIGINT, int_stat); + signal(SIGQUIT, quit_stat); + signal(SIGUSR2, usr2_stat); + + return proc_status; +} diff --git a/envy24control/profiles.c b/envy24control/profiles.c new file mode 100644 index 0000000..c3bff6c --- /dev/null +++ b/envy24control/profiles.c @@ -0,0 +1,1165 @@ +/* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 ((*cfgfile != DEFAULT_PROFILERC) && (*cfgfile != SYS_PROFILERC)) { + res = -ENOENT; + } else if ((*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 = 0; 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 > MAX_PROFILES) { + fprintf(stderr, "profile number '%d' is incorrect. the maximum profile number will be '%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 (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 (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; +} diff --git a/envy24control/profiles.h b/envy24control/profiles.h new file mode 100644 index 0000000..2063bff --- /dev/null +++ b/envy24control/profiles.h @@ -0,0 +1,84 @@ +#ifndef __PROFILES_H__ +#define __PROFILES_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PROGRAM_NAME +#define PROGRAM_NAME "envy24control" +#endif + +#ifndef MAX_PROFILES +#define MAX_PROFILES 4 +#endif + +#ifndef MAX_PROFILE_NAME_LENGTH +#define MAX_PROFILE_NAME_LENGTH 20 +#endif + +#ifndef DEFAULT_PROFILERC +#define DEFAULT_PROFILERC "~/"PROGRAM_NAME"/profiles.conf" +#endif + +#ifndef SYS_PROFILERC +#define SYS_PROFILERC "/etc/"PROGRAM_NAME"/profiles.conf" +#endif + +#define PROFILE_NAME_FIELD_LENGTH MAX_PROFILE_NAME_LENGTH + 1 + +#define PROFILE_HEADER_TEMPL "[ Profile # ]" +#define CARD_HEADER_TEMPL "< Card # >" +#define CARD_FOOTER_TEMPL "< /CARD # >" +#define PROFILE_NAME_TEMPL "{ /$/ }" + +#define PLACE_HOLDER_NUM '#' +#define PLACE_HOLDER_STR '$' + +/* max 32k for every profile */ +#define MAX_PROFILE_SIZE 32768 +#define MAX_SEARCH_FIELD_LENGTH 1024 +#define MAX_FILE_NAME_LENGTH 1024 +#define MAX_NUM_STR_LENGTH 10 +#define TOKEN_SEP "|" +#define SEP_CHAR ' ' + +#ifndef NOTFOUND +#define NOTFOUND -1 +#endif + +#define ALSACTL_OP_STORE "store" +#define ALSACTL_OP_RESTORE "restore" + +#define DIR_CREA_MODE "0755" // this must be a string +#define FILE_CREA_MODE 0644 // this must be a octal number + +/* max count of parameters for new_process + * !first parameter will be the name of the external programm + * - last parameter will be NULL + */ +#define MAX_PARAM 10 + +/* the place from mkdir */ +#ifndef MKDIR +#define MKDIR "/bin/mkdir" +#endif + +/* the place from alsactl */ +#ifndef ALSACTL +#define ALSACTL "/usr/sbin/alsactl" +#endif + +#ifndef __PROFILES_C__ +extern int save_restore(const char * const operation, const int profile_number, const int card_number, char * cfgfile, const char * const profile_name); +extern char *get_profile_name(const int profile_number, const int card_number, char * cfgfile); +extern int get_profile_number(const char * const profile_name, const int card_number, char * cfgfile); +extern int delete_card(const int card_number, char * const cfgfile); +#endif + +#endif /* __PROFILES_H__ */ -- 2.47.1