📄 mailcap.c
字号:
/* RFC1524 (mailcap file) implementation *//* $Id: mailcap.c,v 1.87.2.4 2005/05/01 22:03:23 jonas Exp $ *//* This file contains various functions for implementing a fair subset of * rfc1524. * * The rfc1524 defines a format for the Multimedia Mail Configuration, which is * the standard mailcap file format under Unix which specifies what external * programs should be used to view/compose/edit multimedia files based on * content type. * * Copyright (C) 1996-2000 Michael R. Elkins <me@cs.hmc.edu> * Copyright (c) 2002-2004 The ELinks project * * This file was hijacked from the Mutt project <URL:http://www.mutt.org> * (version 1.4) on Saturday the 7th December 2002. It has been heavily * elinksified. */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <ctype.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include "elinks.h"#include "config/options.h"#include "intl/gettext/libintl.h"#include "mime/backend/common.h"#include "mime/backend/mailcap.h"#include "mime/mime.h"#include "modules/module.h"#include "osdep/osdep.h" /* For exe() */#include "sched/session.h"#include "util/file.h"#include "util/hash.h"#include "util/lists.h"#include "util/memory.h"#include "util/string.h"struct mailcap_hash_item { /* The entries associated with the type */ struct list_head entries; /* -> struct mailcap_entry */ /* The content type of all @entries. Must be last! */ unsigned char type[1];};struct mailcap_entry { LIST_HEAD(struct mailcap_entry); /* To verify if command qualifies. Cannot contain %s formats. */ unsigned char *testcommand; /* Used to inform the user of the type or handler. */ unsigned char *description; /* Used to determine between an exact match and a wildtype match. Lower * is better. Increased for each sourced file. */ unsigned int priority; /* Whether the program "blocks" the term. */ unsigned int needsterminal:1; /* If "| ${PAGER}" should be added. It would of course be better to * pipe the output into a buffer and let ELinks display it but this * will have to do for now. */ unsigned int copiousoutput:1; /* The 'raw' unformatted (view)command from the mailcap files. */ unsigned char command[1];};enum mailcap_option { MAILCAP_TREE, MAILCAP_ENABLE, MAILCAP_PATH, MAILCAP_ASK, MAILCAP_DESCRIPTION, MAILCAP_PRIORITIZE, MAILCAP_OPTIONS};static struct option_info mailcap_options[] = { INIT_OPT_TREE("mime", N_("Mailcap"), "mailcap", 0, N_("Options for mailcap support.")), INIT_OPT_BOOL("mime.mailcap", N_("Enable"), "enable", 0, 1, N_("Enable mailcap support.")), INIT_OPT_STRING("mime.mailcap", N_("Path"), "path", 0, DEFAULT_MAILCAP_PATH, N_("Mailcap search path. Colon-separated list of files.\n" "Leave as \"\" to use MAILCAP environment variable instead.")), INIT_OPT_BOOL("mime.mailcap", N_("Ask before opening"), "ask", 0, 1, N_("Ask before using the handlers defined by mailcap.")), INIT_OPT_INT("mime.mailcap", N_("Type query string"), "description", 0, 0, 2, 0, N_("Type of description to show in \"what to do with this file\"\n" "query dialog:\n" "0 is show \"mailcap\"\n" "1 is show program to be run\n" "2 is show mailcap description field if any; \"mailcap\" otherwise")), INIT_OPT_BOOL("mime.mailcap", N_("Prioritize entries by file"), "prioritize", 0, 1, N_("Prioritize entries by the order of the files in the mailcap\n" "path. This means that wildcard entries (like: image/*) will\n" "also be checked before deciding the handler.")), NULL_OPTION_INFO,};#define get_opt_mailcap(which) mailcap_options[(which)].option#define get_mailcap(which) get_opt_mailcap(which).value#define get_mailcap_ask() get_mailcap(MAILCAP_ASK).number#define get_mailcap_description() get_mailcap(MAILCAP_DESCRIPTION).number#define get_mailcap_enable() get_mailcap(MAILCAP_ENABLE).number#define get_mailcap_prioritize() get_mailcap(MAILCAP_PRIORITIZE).number#define get_mailcap_path() get_mailcap(MAILCAP_PATH).string/* State variables */static struct hash *mailcap_map = NULL;static int mailcap_map_size = 0;static inline voiddone_mailcap_entry(struct mailcap_entry *entry){ if (!entry) return; mem_free_if(entry->testcommand); mem_free_if(entry->description); mem_free(entry);}/* Takes care of all initialization of mailcap entries. * Clear memory to make freeing it safer later and we get * needsterminal and copiousoutput initialized for free. */static inline struct mailcap_entry *init_mailcap_entry(unsigned char *command, int priority){ struct mailcap_entry *entry; int commandlen = strlen(command); entry = mem_calloc(1, sizeof(*entry) + commandlen); if (!entry) return NULL; memcpy(entry->command, command, commandlen); entry->priority = priority; return entry;}static inline voidadd_mailcap_entry(struct mailcap_entry *entry, unsigned char *type, int typelen){ struct mailcap_hash_item *mitem; struct hash_item *item; /* Time to get the entry into the mailcap_map */ /* First check if the type is already checked in */ item = get_hash_item(mailcap_map, type, typelen); if (!item) { mitem = mem_alloc(sizeof(*mitem) + typelen); if (!mitem) { done_mailcap_entry(entry); return; } safe_strncpy(mitem->type, type, typelen + 1); init_list(mitem->entries); item = add_hash_item(mailcap_map, mitem->type, typelen, mitem); if (!item) { mem_free(mitem); done_mailcap_entry(entry); return; } } else if (item->value) { mitem = item->value; } else { done_mailcap_entry(entry); return; } add_to_list_end(mitem->entries, entry); mailcap_map_size++;}/* Parsing of a RFC1524 mailcap file *//* The format is: * * base/type; command; extradefs * * type can be * for matching all; base with no /type is an implicit * wildcard; command contains a %s for the filename to pass, default to pipe on * stdin; extradefs are of the form: * * def1="definition"; def2="define \;"; * * line wraps with a \ at the end of the line, # for comments. *//* TODO handle default pipe. Maybe by prepending "cat |" to the command. *//* Returns a NULL terminated RFC 1524 field, while modifying @next to point * to the next field. */static unsigned char *get_mailcap_field(unsigned char **next){ unsigned char *field; unsigned char *fieldend; if (!next || !*next) return NULL; field = *next; skip_space(field); fieldend = field; /* End field at the next occurence of ';' but not escaped '\;' */ do { /* Handle both if ';' is the first char or if it's escaped */ if (*fieldend == ';') fieldend++; fieldend = strchr(fieldend, ';'); } while (fieldend && *(fieldend-1) == '\\'); if (fieldend) { *fieldend = '\0'; *next = fieldend; fieldend--; (*next)++; skip_space(*next); } else { *next = NULL; fieldend = field + strlen(field) - 1; } /* Remove trailing whitespace */ while (field <= fieldend && isspace(*fieldend)) *fieldend-- = '\0'; return field;}/* Parses specific fields (ex: the '=TestCommand' part of 'test=TestCommand'). * Expects that @field is pointing right after the specifier (ex: 'test' * above). Allocates and returns a NULL terminated token, or NULL if parsing * fails. */static unsigned char *get_mailcap_field_text(unsigned char *field){ skip_space(field); if (*field == '=') { field++; skip_space(field); return stracpy(field); } return NULL;}/* Parse optional extra definitions. Zero return value means syntax error */static inline intparse_optional_fields(struct mailcap_entry *entry, unsigned char *line){ while (0xf131d5) { unsigned char *field = get_mailcap_field(&line); if (!field) break; if (!strncasecmp(field, "needsterminal", 13)) { entry->needsterminal = 1; } else if (!strncasecmp(field, "copiousoutput", 13)) { entry->copiousoutput = 1; } else if (!strncasecmp(field, "test", 4)) { entry->testcommand = get_mailcap_field_text(field + 4); if (!entry->testcommand) return 0; /* Find out wether testing requires filename */ for (field = entry->testcommand; *field; field++) if (*field == '%' && *(field+1) == 's') { mem_free(entry->testcommand); return 0; } } else if (!strncasecmp(field, "description", 11)) { entry->description = get_mailcap_field_text(field + 11); if (!entry->description) return 0; } } return 1;}/* Parses whole mailcap files line-by-line adding entries to the map * assigning them the given @priority */static voidparse_mailcap_file(unsigned char *filename, unsigned int priority){ FILE *file = fopen(filename, "rb"); unsigned char *line = NULL; size_t linelen = MAX_STR_LEN; int lineno = 1; if (!file) return; while ((line = file_read_line(line, &linelen, file, &lineno))) { struct mailcap_entry *entry; unsigned char *linepos; unsigned char *command; unsigned char *basetypeend; unsigned char *type; int typelen; /* Ignore comments */ if (*line == '#') continue; linepos = line; /* Get type */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -