/*
* Copyright (c) 1999, 2000 Motoyuki Kasahara.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification, immediately at the beginning of the file.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <regex.h>
#include "pccard_alias.h"
/*
* Maximum length of a line in an alias file.
*/
#define PCCARD_MAX_ALIAS_LINE_LENGTH 511
/*
* The number of arguments in a correct alias definition.
*/
#define PCCARD_ALIAS_ARGUMENTS 4
/*
* Unexported function.
*/
static int pccard_alias_regexp(const char *, const char *);
/*
* Message handlers.
*/
extern void (*pccard_output_error_message)(const char *, ...);
extern void (*pccard_output_warning_message)(const char *, ...);
extern void (*pccard_output_debug_message)(const char *, ...);
/*
* Debug flag.
*/
extern int pccard_debug_flag;
/*
* Find an alias file for an alias matched to `manufacturer' and
* `version'.
*
* If a matched alias entry is found, the alias name is copied to
* `alias' and 1 is returned. The alias name is at most
* `PCCARD_MAX_ALIAS_LENGTH' charactrers. If no matched entry is
* found, 0 is returned.
*/
int
pccard_search_alias_file(const char *file_name, char *alias,
const char *manufacturer, const char *version)
{
FILE *file;
char line[PCCARD_MAX_ALIAS_LINE_LENGTH + 1];
char *inp, *outp;
int too_long_line;
int backslash_escaped;
int single_quoted;
int double_quoted;
int separator_preceded;
char *arguments[PCCARD_ALIAS_ARGUMENTS];
int argument_count;
int line_number;
int is_found = 0;
*alias = '\0';
/*
* Open an alias file.
*/
file = fopen(file_name, "r");
if (file == NULL) {
pccard_output_error_message("failed to open the alias file, %s: %s",
strerror(errno), file_name);
return 0;
}
/*
* Read the alias file.
*/
too_long_line = 0;
line_number = 1;
while (fgets(line, PCCARD_MAX_ALIAS_LINE_LENGTH + 1, file) != NULL) {
/*
* If a line is too long, skip to the beginning of the next line.
*/
if (strchr(line, '\n') == NULL
&& strlen(line) == PCCARD_MAX_ALIAS_LINE_LENGTH) {
too_long_line = 1;
pccard_output_warning_message("the line too long, at line %d: %s",
line_number, file_name);
continue;
} else if (too_long_line) {
too_long_line = 0;
continue;
}
/*
* Ignore whitespaces in the beginning of the line.
*/
inp = line;
outp = line;
while (*inp == ' ' || *inp == '\t')
inp++;
/*
* Split the line in arguments.
*/
argument_count = 0;
backslash_escaped = 0;
single_quoted = 0;
double_quoted = 0;
separator_preceded = 1;
for (;;) {
/*
* Stop parsing if a newline or a non-quoted `#' occurs.
*/
if (*inp == '\n' || *inp == '\0')
break;
if (*inp == '#' && !backslash_escaped && !single_quoted
&& !double_quoted)
break;
/*
* Here is the beginning of an argument.
*/
if (separator_preceded) {
if (argument_count < PCCARD_ALIAS_ARGUMENTS)
arguments[argument_count] = outp;
argument_count++;
separator_preceded = 0;
}
switch (*inp) {
case '\\':
/*
* Backslash is escape character.
*/
if (!backslash_escaped && !single_quoted && !double_quoted)
backslash_escaped = 1;
else if (!backslash_escaped && double_quoted
&& (*(inp + 1) == '$' || *(inp + 1) == '`'
|| *(inp + 1) == '\"' || *(inp + 1) == '\\')) {
backslash_escaped = 1;
} else {
backslash_escaped = 0;
*outp++ = *inp;
}
break;
case ' ':
case '\t':
/*
* Whitespace separates arguments.
*/
if (backslash_escaped || single_quoted || double_quoted)
*outp++ = *inp;
else {
inp++;
*outp++ = '\0';
while (*inp == ' ' || *inp == '\t')
inp++;
inp--;
separator_preceded = 1;
}
backslash_escaped = 0;
break;
case '\'':
/*
* Single quotation mark is used for quotation.
*/
if (!backslash_escaped && !double_quoted)
single_quoted = !single_quoted;
else
*outp++ = *inp;
backslash_escaped = 0;
break;
case '\"':
/*
* Double quotation mark is used for quotation.
*/
if (!backslash_escaped && !single_quoted)
double_quoted = !double_quoted;
else
*outp++ = *inp;
backslash_escaped = 0;
break;
default:
*outp++ = *inp;
backslash_escaped = 0;
break;
}
inp++;
}
*outp = '\0';
line_number++;
/*
* Check for parsing status.
*/
if (argument_count == 0)
continue;
if (argument_count > PCCARD_ALIAS_ARGUMENTS) {
pccard_output_warning_message("too many arguments, at line %d: %s",
line_number, file_name);
}
if (argument_count < PCCARD_ALIAS_ARGUMENTS
|| backslash_escaped || single_quoted || double_quoted) {
pccard_output_warning_message("unexpected end of line, "
"at line %d: %s", line_number, file_name);
continue;
}
if (strcmp(arguments[0], "alias") != 0) {
pccard_output_warning_message("unknown directive `%s', "
"at line %d: %s", line_number, file_name, arguments[0]);
continue;
}
if (pccard_alias_regexp(arguments[2], manufacturer)
&& pccard_alias_regexp(arguments[3], version)) {
strncpy(alias, arguments[1], PCCARD_MAX_ALIAS_LENGTH);
*(alias + PCCARD_MAX_ALIAS_LENGTH) = '\0';
is_found = 1;
break;
}
}
/*
* Check for file read error.
*/
if (ferror(file)) {
pccard_output_error_message("failed to read the alias file, %s: %s",
strerror(errno), file_name);
fclose(file);
return 0;
}
/*
* Close the file.
*/
fclose(file);
/*
* Output a debug message if the debug flag is enabled.
*/
if (pccard_debug_flag) {
if (is_found) {
pccard_output_debug_message("alias found: "
"alias=%s, manufacturer=%s, version=%s, file=%s",
alias, manufacturer, version, file_name);
} else {
pccard_output_debug_message("alias not found: "
"manufacturer=%s, version=%s, file=%s",
manufacturer, version, file_name);
}
}
return is_found;
}
/*
* Determine whether `string' is matched to the regular expression
* `expression'. It returns 1 if it does, 0 if doesn't.
*/
static int
pccard_alias_regexp(const char *expression, const char *string)
{
regex_t ex;
int is_matched = 0;
if (regcomp(&ex, expression, REG_EXTENDED | REG_NOSUB) == 0) {
is_matched = (regexec(&ex, string, 0, NULL, 0) == 0) ? 1 : 0;
regfree(&ex);
}
return is_matched;
}
/*
* For test:
*/
#ifdef TEST
int
main(int argc, char *argv[])
{
char buffer[256];
if (argc != 4) {
fprintf(stderr, "bad usage\n");
exit(1);
}
buffer[0] = '\0';
if (pccard_alias(argv[1], buffer, argv[2], argv[3]) == -1)
exit(1);
printf("%s\n", buffer);
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1