📄 argp-help.c
字号:
entry->num = 0; entry->short_options = so; entry->group = cur_group = o->group ? o->group : ((!o->name && !o->key) ? cur_group + 1 : cur_group); entry->cluster = cluster; entry->argp = argp; do { entry->num++; if (oshort (o) && ! find_char (o->key, hol->short_options, so)) /* O has a valid short option which hasn't already been used.*/ *so++ = o->key; o++; } while (! oend (o) && oalias (o)); } *so = '\0'; /* null terminated so we can find the length */ } return hol;}/* Add a new cluster to HOL, with the given GROUP and HEADER (taken from the associated argp child list entry), INDEX, and PARENT, and return a pointer to it. ARGP is the argp that this cluster results from. */static struct hol_cluster *hol_add_cluster (struct hol *hol, int group, const char *header, int index, struct hol_cluster *parent, const struct argp *argp){ struct hol_cluster *cl = malloc (sizeof (struct hol_cluster)); if (cl) { cl->group = group; cl->header = header; cl->index = index; cl->parent = parent; cl->argp = argp; cl->next = hol->clusters; hol->clusters = cl; } return cl;}/* Free HOL and any resources it uses. */static voidhol_free (struct hol *hol){ struct hol_cluster *cl = hol->clusters; while (cl) { struct hol_cluster *next = cl->next; free (cl); cl = next; } if (hol->num_entries > 0) { free (hol->entries); free (hol->short_options); } free (hol);}static inline inthol_entry_short_iterate (const struct hol_entry *entry, int (*func)(const struct argp_option *opt, const struct argp_option *real, void *cookie), void *cookie){ unsigned nopts; int val = 0; const struct argp_option *opt, *real = entry->opt; char *so = entry->short_options; for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) if (oshort (opt) && *so == opt->key) { if (!oalias (opt)) real = opt; if (ovisible (opt)) val = (*func)(opt, real, cookie); so++; } return val;}static inline inthol_entry_long_iterate (const struct hol_entry *entry, int (*func)(const struct argp_option *opt, const struct argp_option *real, void *cookie), void *cookie){ unsigned nopts; int val = 0; const struct argp_option *opt, *real = entry->opt; for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) if (opt->name) { if (!oalias (opt)) real = opt; if (ovisible (opt)) val = (*func)(opt, real, cookie); } return val;}/* Iterator that returns true for the first short option. */static inline intuntil_short (const struct argp_option *opt, const struct argp_option *real, void *cookie){ return oshort (opt) ? opt->key : 0;}/* Returns the first valid short option in ENTRY, or 0 if there is none. */static charhol_entry_first_short (const struct hol_entry *entry){ return hol_entry_short_iterate (entry, until_short, 0);}/* Returns the first valid long option in ENTRY, or 0 if there is none. */static const char *hol_entry_first_long (const struct hol_entry *entry){ const struct argp_option *opt; unsigned num; for (opt = entry->opt, num = entry->num; num > 0; opt++, num--) if (opt->name && ovisible (opt)) return opt->name; return 0;}/* Returns the entry in HOL with the long option name NAME, or 0 if there is none. */static struct hol_entry *hol_find_entry (struct hol *hol, const char *name){ struct hol_entry *entry = hol->entries; unsigned num_entries = hol->num_entries; while (num_entries-- > 0) { const struct argp_option *opt = entry->opt; unsigned num_opts = entry->num; while (num_opts-- > 0) if (opt->name && ovisible (opt) && strcmp (opt->name, name) == 0) return entry; else opt++; entry++; } return 0;}/* If an entry with the long option NAME occurs in HOL, set it's special sort position to GROUP. */static voidhol_set_group (struct hol *hol, const char *name, int group){ struct hol_entry *entry = hol_find_entry (hol, name); if (entry) entry->group = group;}/* Order by group: 0, 1, 2, ..., n, -m, ..., -2, -1. EQ is what to return if GROUP1 and GROUP2 are the same. */static intgroup_cmp (int group1, int group2, int eq){ if (group1 == group2) return eq; else if ((group1 < 0 && group2 < 0) || (group1 >= 0 && group2 >= 0)) return group1 - group2; else return group2 - group1;}/* Compare clusters CL1 & CL2 by the order that they should appear in output. */static inthol_cluster_cmp (const struct hol_cluster *cl1, const struct hol_cluster *cl2){ /* If one cluster is deeper than the other, use its ancestor at the same level, so that finding the common ancestor is straightforward. */ while (cl1->depth < cl2->depth) cl1 = cl1->parent; while (cl2->depth < cl1->depth) cl2 = cl2->parent; /* Now reduce both clusters to their ancestors at the point where both have a common parent; these can be directly compared. */ while (cl1->parent != cl2->parent) cl1 = cl1->parent, cl2 = cl2->parent; return group_cmp (cl1->group, cl2->group, cl2->index - cl1->index);}/* Return the ancestor of CL that's just below the root (i.e., has a parent of 0). */static struct hol_cluster *hol_cluster_base (struct hol_cluster *cl){ while (cl->parent) cl = cl->parent; return cl;}/* Return true if CL1 is a child of CL2. */static inthol_cluster_is_child (const struct hol_cluster *cl1, const struct hol_cluster *cl2){ while (cl1 && cl1 != cl2) cl1 = cl1->parent; return cl1 == cl2;}/* Given the name of a OPTION_DOC option, modifies NAME to start at the tail that should be used for comparisons, and returns true iff it should be treated as a non-option. */static intcanon_doc_option (const char **name){ int non_opt; /* Skip initial whitespace. */ while (isspace (**name)) (*name)++; /* Decide whether this looks like an option (leading `-') or not. */ non_opt = (**name != '-'); /* Skip until part of name used for sorting. */ while (**name && !isalnum (**name)) (*name)++; return non_opt;}/* Order ENTRY1 & ENTRY2 by the order which they should appear in a help listing. */static inthol_entry_cmp (const struct hol_entry *entry1, const struct hol_entry *entry2){ /* The group numbers by which the entries should be ordered; if either is in a cluster, then this is just the group within the cluster. */ int group1 = entry1->group, group2 = entry2->group; if (entry1->cluster != entry2->cluster) /* The entries are not within the same cluster, so we can't compare them directly, we have to use the appropiate clustering level too. */ if (! entry1->cluster) /* ENTRY1 is at the `base level', not in a cluster, so we have to compare it's group number with that of the base cluster in which ENTRY2 resides. Note that if they're in the same group, the clustered option always comes laster. */ return group_cmp (group1, hol_cluster_base (entry2->cluster)->group, -1); else if (! entry2->cluster) /* Likewise, but ENTRY2's not in a cluster. */ return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1); else /* Both entries are in clusters, we can just compare the clusters. */ return hol_cluster_cmp (entry1->cluster, entry2->cluster); else if (group1 == group2) /* The entries are both in the same cluster and group, so compare them alphabetically. */ { int short1 = hol_entry_first_short (entry1); int short2 = hol_entry_first_short (entry2); int doc1 = odoc (entry1->opt); int doc2 = odoc (entry2->opt); const char *long1 = hol_entry_first_long (entry1); const char *long2 = hol_entry_first_long (entry2); if (doc1) doc1 = canon_doc_option (&long1); if (doc2) doc2 = canon_doc_option (&long2); if (doc1 != doc2) /* `documentation' options always follow normal options (or documentation options that *look* like normal options). */ return doc1 - doc2; else if (!short1 && !short2 && long1 && long2) /* Only long options. */ return __strcasecmp (long1, long2); else /* Compare short/short, long/short, short/long, using the first character of long options. Entries without *any* valid options (such as options with OPTION_HIDDEN set) will be put first, but as they're not displayed, it doesn't matter where they are. */ { char first1 = short1 ? short1 : long1 ? *long1 : 0; char first2 = short2 ? short2 : long2 ? *long2 : 0; int lower_cmp = tolower (first1) - tolower (first2); /* Compare ignoring case, except when the options are both the same letter, in which case lower-case always comes first. */ return lower_cmp ? lower_cmp : first2 - first1; } } else /* Within the same cluster, but not the same group, so just compare groups. */ return group_cmp (group1, group2, 0);}/* Version of hol_entry_cmp with correct signature for qsort. */static inthol_entry_qcmp (const void *entry1_v, const void *entry2_v){ return hol_entry_cmp (entry1_v, entry2_v);}/* Sort HOL by group and alphabetically by option name (with short options taking precedence over long). Since the sorting is for display purposes only, the shadowing of options isn't effected. */static voidhol_sort (struct hol *hol){ if (hol->num_entries > 0) qsort (hol->entries, hol->num_entries, sizeof (struct hol_entry), hol_entry_qcmp);}/* Append MORE to HOL, destroying MORE in the process. Options in HOL shadow any in MORE with the same name. */static voidhol_append (struct hol *hol, struct hol *more){ struct hol_cluster **cl_end = &hol->clusters; /* Steal MORE's cluster list, and add it to the end of HOL's. */ while (*cl_end) cl_end = &(*cl_end)->next; *cl_end = more->clusters; more->clusters = 0; /* Merge entries. */ if (more->num_entries > 0) if (hol->num_entries == 0) { hol->num_entries = more->num_entries; hol->entries = more->entries; hol->short_options = more->short_options; more->num_entries = 0; /* Mark MORE's fields as invalid. */ } else /* append the entries in MORE to those in HOL, taking care to only add non-shadowed SHORT_OPTIONS values. */ { unsigned left; char *so, *more_so; struct hol_entry *e; unsigned num_entries = hol->num_entries + more->num_entries; struct hol_entry *entries = malloc (num_entries * sizeof (struct hol_entry)); unsigned hol_so_len = strlen (hol->short_options); char *short_options = malloc (hol_so_len + strlen (more->short_options) + 1); memcpy (entries, hol->entries, hol->num_entries * sizeof (struct hol_entry)); memcpy (entries + hol->num_entries, more->entries, more->num_entries * sizeof (struct hol_entry)); memcpy (short_options, hol->short_options, hol_so_len); /* Fix up the short options pointers from HOL. */ for (e = entries, left = hol->num_entries; left > 0; e++, left--) e->short_options += (short_options - hol->short_options); /* Now add the short options from MORE, fixing up its entries too. */ so = short_options + hol_so_len; more_so = more->short_options; for (left = more->num_entries; left > 0; e++, left--) { int opts_left; const struct argp_option *opt; e->short_options = so; for (opts_left = e->num, opt = e->opt; opts_left; opt++, opts_left--) { int ch = *more_so; if (oshort (opt) && ch == opt->key) /* The next short option in MORE_SO, CH, is from OPT. */ { if (! find_char (ch, short_options, short_options + hol_so_len)) /* The short option CH isn't shadowed by HOL's options, so add it to the sum. */ *so++ = ch; more_so++; } } } *so = '\0'; free (hol->entries); free (hol->short_options); hol->entries = entries; hol->num_entries = num_entries; hol->short_options = short_options; } hol_free (more);}/* Inserts enough spaces to make sure STREAM is at column COL. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -