/* * 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 #include #include #include #include #include #include #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