mirror of
https://github.com/KevinMidboe/linguist.git
synced 2025-10-29 17:50:22 +00:00
Rename samples subdirectories
This commit is contained in:
25
samples/C/blob.c
Normal file
25
samples/C/blob.c
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "cache.h"
|
||||
#include "blob.h"
|
||||
|
||||
const char *blob_type = "blob";
|
||||
|
||||
struct blob *lookup_blob(const unsigned char *sha1)
|
||||
{
|
||||
struct object *obj = lookup_object(sha1);
|
||||
if (!obj)
|
||||
return create_object(sha1, OBJ_BLOB, alloc_blob_node());
|
||||
if (!obj->type)
|
||||
obj->type = OBJ_BLOB;
|
||||
if (obj->type != OBJ_BLOB) {
|
||||
error("Object %s is a %s, not a blob",
|
||||
sha1_to_hex(sha1), typename(obj->type));
|
||||
return NULL;
|
||||
}
|
||||
return (struct blob *) obj;
|
||||
}
|
||||
|
||||
int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)
|
||||
{
|
||||
item->object.parsed = 1;
|
||||
return 0;
|
||||
}
|
||||
25
samples/C/blob.h
Normal file
25
samples/C/blob.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef BLOB_H
|
||||
#define BLOB_H
|
||||
|
||||
#include "object.h"
|
||||
|
||||
extern const char *blob_type;
|
||||
|
||||
struct blob {
|
||||
struct object object;
|
||||
};
|
||||
|
||||
struct blob *lookup_blob(const unsigned char *sha1);
|
||||
|
||||
int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);
|
||||
|
||||
/**
|
||||
* Blobs do not contain references to other objects and do not have
|
||||
* structured data that needs parsing. However, code may use the
|
||||
* "parsed" bit in the struct object for a blob to determine whether
|
||||
* its content has been found to actually be available, so
|
||||
* parse_blob_buffer() is used (by object.c) to flag that the object
|
||||
* has been read successfully from the database.
|
||||
**/
|
||||
|
||||
#endif /* BLOB_H */
|
||||
99
samples/C/cache.c
Normal file
99
samples/C/cache.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "repository.h"
|
||||
#include "commit.h"
|
||||
#include "thread-utils.h"
|
||||
#include "util.h"
|
||||
#include "cache.h"
|
||||
|
||||
int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
|
||||
{
|
||||
if (size < 8)
|
||||
size = 8;
|
||||
size = git__size_t_powerof2(size);
|
||||
|
||||
cache->size_mask = size - 1;
|
||||
cache->lru_count = 0;
|
||||
cache->free_obj = free_ptr;
|
||||
|
||||
git_mutex_init(&cache->lock);
|
||||
|
||||
cache->nodes = git__malloc(size * sizeof(git_cached_obj *));
|
||||
GITERR_CHECK_ALLOC(cache->nodes);
|
||||
|
||||
memset(cache->nodes, 0x0, size * sizeof(git_cached_obj *));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_cache_free(git_cache *cache)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < (cache->size_mask + 1); ++i) {
|
||||
if (cache->nodes[i] != NULL)
|
||||
git_cached_obj_decref(cache->nodes[i], cache->free_obj);
|
||||
}
|
||||
|
||||
git__free(cache->nodes);
|
||||
}
|
||||
|
||||
void *git_cache_get(git_cache *cache, const git_oid *oid)
|
||||
{
|
||||
uint32_t hash;
|
||||
git_cached_obj *node = NULL, *result = NULL;
|
||||
|
||||
memcpy(&hash, oid->id, sizeof(hash));
|
||||
|
||||
git_mutex_lock(&cache->lock);
|
||||
{
|
||||
node = cache->nodes[hash & cache->size_mask];
|
||||
|
||||
if (node != NULL && git_oid_cmp(&node->oid, oid) == 0) {
|
||||
git_cached_obj_incref(node);
|
||||
result = node;
|
||||
}
|
||||
}
|
||||
git_mutex_unlock(&cache->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void *git_cache_try_store(git_cache *cache, void *_entry)
|
||||
{
|
||||
git_cached_obj *entry = _entry;
|
||||
uint32_t hash;
|
||||
|
||||
memcpy(&hash, &entry->oid, sizeof(uint32_t));
|
||||
|
||||
/* increase the refcount on this object, because
|
||||
* the cache now owns it */
|
||||
git_cached_obj_incref(entry);
|
||||
|
||||
git_mutex_lock(&cache->lock);
|
||||
{
|
||||
git_cached_obj *node = cache->nodes[hash & cache->size_mask];
|
||||
|
||||
if (node == NULL) {
|
||||
cache->nodes[hash & cache->size_mask] = entry;
|
||||
} else if (git_oid_cmp(&node->oid, &entry->oid) == 0) {
|
||||
git_cached_obj_decref(entry, cache->free_obj);
|
||||
entry = node;
|
||||
} else {
|
||||
git_cached_obj_decref(node, cache->free_obj);
|
||||
cache->nodes[hash & cache->size_mask] = entry;
|
||||
}
|
||||
}
|
||||
git_mutex_unlock(&cache->lock);
|
||||
|
||||
/* increase the refcount again, because we are
|
||||
* returning it to the user */
|
||||
git_cached_obj_incref(entry);
|
||||
|
||||
return entry;
|
||||
}
|
||||
1228
samples/C/commit.c
Normal file
1228
samples/C/commit.c
Normal file
File diff suppressed because it is too large
Load Diff
226
samples/C/commit.h
Normal file
226
samples/C/commit.h
Normal file
@@ -0,0 +1,226 @@
|
||||
#ifndef COMMIT_H
|
||||
#define COMMIT_H
|
||||
|
||||
#include "object.h"
|
||||
#include "tree.h"
|
||||
#include "strbuf.h"
|
||||
#include "decorate.h"
|
||||
|
||||
struct commit_list {
|
||||
struct commit *item;
|
||||
struct commit_list *next;
|
||||
};
|
||||
|
||||
struct commit {
|
||||
struct object object;
|
||||
void *util;
|
||||
unsigned int indegree;
|
||||
unsigned long date;
|
||||
struct commit_list *parents;
|
||||
struct tree *tree;
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
extern int save_commit_buffer;
|
||||
extern const char *commit_type;
|
||||
|
||||
/* While we can decorate any object with a name, it's only used for commits.. */
|
||||
extern struct decoration name_decoration;
|
||||
struct name_decoration {
|
||||
struct name_decoration *next;
|
||||
int type;
|
||||
char name[1];
|
||||
};
|
||||
|
||||
struct commit *lookup_commit(const unsigned char *sha1);
|
||||
struct commit *lookup_commit_reference(const unsigned char *sha1);
|
||||
struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
|
||||
int quiet);
|
||||
struct commit *lookup_commit_reference_by_name(const char *name);
|
||||
|
||||
/*
|
||||
* Look up object named by "sha1", dereference tag as necessary,
|
||||
* get a commit and return it. If "sha1" does not dereference to
|
||||
* a commit, use ref_name to report an error and die.
|
||||
*/
|
||||
struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name);
|
||||
|
||||
int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size);
|
||||
int parse_commit(struct commit *item);
|
||||
|
||||
/* Find beginning and length of commit subject. */
|
||||
int find_commit_subject(const char *commit_buffer, const char **subject);
|
||||
|
||||
struct commit_list *commit_list_insert(struct commit *item,
|
||||
struct commit_list **list);
|
||||
struct commit_list **commit_list_append(struct commit *commit,
|
||||
struct commit_list **next);
|
||||
unsigned commit_list_count(const struct commit_list *l);
|
||||
struct commit_list *commit_list_insert_by_date(struct commit *item,
|
||||
struct commit_list **list);
|
||||
void commit_list_sort_by_date(struct commit_list **list);
|
||||
|
||||
void free_commit_list(struct commit_list *list);
|
||||
|
||||
/* Commit formats */
|
||||
enum cmit_fmt {
|
||||
CMIT_FMT_RAW,
|
||||
CMIT_FMT_MEDIUM,
|
||||
CMIT_FMT_DEFAULT = CMIT_FMT_MEDIUM,
|
||||
CMIT_FMT_SHORT,
|
||||
CMIT_FMT_FULL,
|
||||
CMIT_FMT_FULLER,
|
||||
CMIT_FMT_ONELINE,
|
||||
CMIT_FMT_EMAIL,
|
||||
CMIT_FMT_USERFORMAT,
|
||||
|
||||
CMIT_FMT_UNSPECIFIED
|
||||
};
|
||||
|
||||
struct pretty_print_context {
|
||||
enum cmit_fmt fmt;
|
||||
int abbrev;
|
||||
const char *subject;
|
||||
const char *after_subject;
|
||||
int preserve_subject;
|
||||
enum date_mode date_mode;
|
||||
unsigned date_mode_explicit:1;
|
||||
int need_8bit_cte;
|
||||
int show_notes;
|
||||
struct reflog_walk_info *reflog_info;
|
||||
const char *output_encoding;
|
||||
};
|
||||
|
||||
struct userformat_want {
|
||||
unsigned notes:1;
|
||||
};
|
||||
|
||||
extern int has_non_ascii(const char *text);
|
||||
struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
|
||||
extern char *logmsg_reencode(const struct commit *commit,
|
||||
const char *output_encoding);
|
||||
extern char *reencode_commit_message(const struct commit *commit,
|
||||
const char **encoding_p);
|
||||
extern void get_commit_format(const char *arg, struct rev_info *);
|
||||
extern const char *format_subject(struct strbuf *sb, const char *msg,
|
||||
const char *line_separator);
|
||||
extern void userformat_find_requirements(const char *fmt, struct userformat_want *w);
|
||||
extern void format_commit_message(const struct commit *commit,
|
||||
const char *format, struct strbuf *sb,
|
||||
const struct pretty_print_context *context);
|
||||
extern void pretty_print_commit(const struct pretty_print_context *pp,
|
||||
const struct commit *commit,
|
||||
struct strbuf *sb);
|
||||
extern void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
|
||||
struct strbuf *sb);
|
||||
void pp_user_info(const struct pretty_print_context *pp,
|
||||
const char *what, struct strbuf *sb,
|
||||
const char *line, const char *encoding);
|
||||
void pp_title_line(const struct pretty_print_context *pp,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
const char *encoding,
|
||||
int need_8bit_cte);
|
||||
void pp_remainder(const struct pretty_print_context *pp,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
int indent);
|
||||
|
||||
|
||||
/** Removes the first commit from a list sorted by date, and adds all
|
||||
* of its parents.
|
||||
**/
|
||||
struct commit *pop_most_recent_commit(struct commit_list **list,
|
||||
unsigned int mark);
|
||||
|
||||
struct commit *pop_commit(struct commit_list **stack);
|
||||
|
||||
void clear_commit_marks(struct commit *commit, unsigned int mark);
|
||||
void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark);
|
||||
|
||||
/*
|
||||
* Performs an in-place topological sort of list supplied.
|
||||
*
|
||||
* invariant of resulting list is:
|
||||
* a reachable from b => ord(b) < ord(a)
|
||||
* in addition, when lifo == 0, commits on parallel tracks are
|
||||
* sorted in the dates order.
|
||||
*/
|
||||
void sort_in_topological_order(struct commit_list ** list, int lifo);
|
||||
|
||||
struct commit_graft {
|
||||
unsigned char sha1[20];
|
||||
int nr_parent; /* < 0 if shallow commit */
|
||||
unsigned char parent[FLEX_ARRAY][20]; /* more */
|
||||
};
|
||||
typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
|
||||
|
||||
struct commit_graft *read_graft_line(char *buf, int len);
|
||||
int register_commit_graft(struct commit_graft *, int);
|
||||
struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
|
||||
|
||||
extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
|
||||
extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup);
|
||||
extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
|
||||
|
||||
extern int register_shallow(const unsigned char *sha1);
|
||||
extern int unregister_shallow(const unsigned char *sha1);
|
||||
extern int for_each_commit_graft(each_commit_graft_fn, void *);
|
||||
extern int is_repository_shallow(void);
|
||||
extern struct commit_list *get_shallow_commits(struct object_array *heads,
|
||||
int depth, int shallow_flag, int not_shallow_flag);
|
||||
|
||||
int is_descendant_of(struct commit *, struct commit_list *);
|
||||
int in_merge_bases(struct commit *, struct commit **, int);
|
||||
|
||||
extern int interactive_add(int argc, const char **argv, const char *prefix, int patch);
|
||||
extern int run_add_interactive(const char *revision, const char *patch_mode,
|
||||
const char **pathspec);
|
||||
|
||||
static inline int single_parent(struct commit *commit)
|
||||
{
|
||||
return commit->parents && !commit->parents->next;
|
||||
}
|
||||
|
||||
struct commit_list *reduce_heads(struct commit_list *heads);
|
||||
|
||||
struct commit_extra_header {
|
||||
struct commit_extra_header *next;
|
||||
char *key;
|
||||
char *value;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
extern void append_merge_tag_headers(struct commit_list *parents,
|
||||
struct commit_extra_header ***tail);
|
||||
|
||||
extern int commit_tree(const struct strbuf *msg, unsigned char *tree,
|
||||
struct commit_list *parents, unsigned char *ret,
|
||||
const char *author, const char *sign_commit);
|
||||
|
||||
extern int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
|
||||
struct commit_list *parents, unsigned char *ret,
|
||||
const char *author, const char *sign_commit,
|
||||
struct commit_extra_header *);
|
||||
|
||||
extern struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **);
|
||||
extern struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
|
||||
|
||||
extern void free_commit_extra_headers(struct commit_extra_header *extra);
|
||||
|
||||
struct merge_remote_desc {
|
||||
struct object *obj; /* the named object, could be a tag */
|
||||
const char *name;
|
||||
};
|
||||
#define merge_remote_util(commit) ((struct merge_remote_desc *)((commit)->util))
|
||||
|
||||
/*
|
||||
* Given "name" from the command line to merge, find the commit object
|
||||
* and return it, while storing merge_remote_desc in its ->util field,
|
||||
* to allow callers to tell if we are told to merge a tag.
|
||||
*/
|
||||
struct commit *get_merge_parent(const char *name);
|
||||
|
||||
extern int parse_signed_commit(const unsigned char *sha1,
|
||||
struct strbuf *message, struct strbuf *signature);
|
||||
#endif /* COMMIT_H */
|
||||
725
samples/C/cpu.c
Normal file
725
samples/C/cpu.c
Normal file
@@ -0,0 +1,725 @@
|
||||
/* CPU control.
|
||||
* (C) 2001, 2002, 2003, 2004 Rusty Russell
|
||||
*
|
||||
* This code is licenced under the GPL.
|
||||
*/
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/oom.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/stop_machine.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include "smpboot.h"
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* Serializes the updates to cpu_online_mask, cpu_present_mask */
|
||||
static DEFINE_MUTEX(cpu_add_remove_lock);
|
||||
|
||||
/*
|
||||
* The following two API's must be used when attempting
|
||||
* to serialize the updates to cpu_online_mask, cpu_present_mask.
|
||||
*/
|
||||
void cpu_maps_update_begin(void)
|
||||
{
|
||||
mutex_lock(&cpu_add_remove_lock);
|
||||
}
|
||||
|
||||
void cpu_maps_update_done(void)
|
||||
{
|
||||
mutex_unlock(&cpu_add_remove_lock);
|
||||
}
|
||||
|
||||
static RAW_NOTIFIER_HEAD(cpu_chain);
|
||||
|
||||
/* If set, cpu_up and cpu_down will return -EBUSY and do nothing.
|
||||
* Should always be manipulated under cpu_add_remove_lock
|
||||
*/
|
||||
static int cpu_hotplug_disabled;
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
|
||||
static struct {
|
||||
struct task_struct *active_writer;
|
||||
struct mutex lock; /* Synchronizes accesses to refcount, */
|
||||
/*
|
||||
* Also blocks the new readers during
|
||||
* an ongoing cpu hotplug operation.
|
||||
*/
|
||||
int refcount;
|
||||
} cpu_hotplug = {
|
||||
.active_writer = NULL,
|
||||
.lock = __MUTEX_INITIALIZER(cpu_hotplug.lock),
|
||||
.refcount = 0,
|
||||
};
|
||||
|
||||
void get_online_cpus(void)
|
||||
{
|
||||
might_sleep();
|
||||
if (cpu_hotplug.active_writer == current)
|
||||
return;
|
||||
mutex_lock(&cpu_hotplug.lock);
|
||||
cpu_hotplug.refcount++;
|
||||
mutex_unlock(&cpu_hotplug.lock);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(get_online_cpus);
|
||||
|
||||
void put_online_cpus(void)
|
||||
{
|
||||
if (cpu_hotplug.active_writer == current)
|
||||
return;
|
||||
mutex_lock(&cpu_hotplug.lock);
|
||||
if (!--cpu_hotplug.refcount && unlikely(cpu_hotplug.active_writer))
|
||||
wake_up_process(cpu_hotplug.active_writer);
|
||||
mutex_unlock(&cpu_hotplug.lock);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(put_online_cpus);
|
||||
|
||||
/*
|
||||
* This ensures that the hotplug operation can begin only when the
|
||||
* refcount goes to zero.
|
||||
*
|
||||
* Note that during a cpu-hotplug operation, the new readers, if any,
|
||||
* will be blocked by the cpu_hotplug.lock
|
||||
*
|
||||
* Since cpu_hotplug_begin() is always called after invoking
|
||||
* cpu_maps_update_begin(), we can be sure that only one writer is active.
|
||||
*
|
||||
* Note that theoretically, there is a possibility of a livelock:
|
||||
* - Refcount goes to zero, last reader wakes up the sleeping
|
||||
* writer.
|
||||
* - Last reader unlocks the cpu_hotplug.lock.
|
||||
* - A new reader arrives at this moment, bumps up the refcount.
|
||||
* - The writer acquires the cpu_hotplug.lock finds the refcount
|
||||
* non zero and goes to sleep again.
|
||||
*
|
||||
* However, this is very difficult to achieve in practice since
|
||||
* get_online_cpus() not an api which is called all that often.
|
||||
*
|
||||
*/
|
||||
static void cpu_hotplug_begin(void)
|
||||
{
|
||||
cpu_hotplug.active_writer = current;
|
||||
|
||||
for (;;) {
|
||||
mutex_lock(&cpu_hotplug.lock);
|
||||
if (likely(!cpu_hotplug.refcount))
|
||||
break;
|
||||
__set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
mutex_unlock(&cpu_hotplug.lock);
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
|
||||
static void cpu_hotplug_done(void)
|
||||
{
|
||||
cpu_hotplug.active_writer = NULL;
|
||||
mutex_unlock(&cpu_hotplug.lock);
|
||||
}
|
||||
|
||||
#else /* #if CONFIG_HOTPLUG_CPU */
|
||||
static void cpu_hotplug_begin(void) {}
|
||||
static void cpu_hotplug_done(void) {}
|
||||
#endif /* #else #if CONFIG_HOTPLUG_CPU */
|
||||
|
||||
/* Need to know about CPUs going up/down? */
|
||||
int __ref register_cpu_notifier(struct notifier_block *nb)
|
||||
{
|
||||
int ret;
|
||||
cpu_maps_update_begin();
|
||||
ret = raw_notifier_chain_register(&cpu_chain, nb);
|
||||
cpu_maps_update_done();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __cpu_notify(unsigned long val, void *v, int nr_to_call,
|
||||
int *nr_calls)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __raw_notifier_call_chain(&cpu_chain, val, v, nr_to_call,
|
||||
nr_calls);
|
||||
|
||||
return notifier_to_errno(ret);
|
||||
}
|
||||
|
||||
static int cpu_notify(unsigned long val, void *v)
|
||||
{
|
||||
return __cpu_notify(val, v, -1, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
|
||||
static void cpu_notify_nofail(unsigned long val, void *v)
|
||||
{
|
||||
BUG_ON(cpu_notify(val, v));
|
||||
}
|
||||
EXPORT_SYMBOL(register_cpu_notifier);
|
||||
|
||||
void __ref unregister_cpu_notifier(struct notifier_block *nb)
|
||||
{
|
||||
cpu_maps_update_begin();
|
||||
raw_notifier_chain_unregister(&cpu_chain, nb);
|
||||
cpu_maps_update_done();
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_cpu_notifier);
|
||||
|
||||
/**
|
||||
* clear_tasks_mm_cpumask - Safely clear tasks' mm_cpumask for a CPU
|
||||
* @cpu: a CPU id
|
||||
*
|
||||
* This function walks all processes, finds a valid mm struct for each one and
|
||||
* then clears a corresponding bit in mm's cpumask. While this all sounds
|
||||
* trivial, there are various non-obvious corner cases, which this function
|
||||
* tries to solve in a safe manner.
|
||||
*
|
||||
* Also note that the function uses a somewhat relaxed locking scheme, so it may
|
||||
* be called only for an already offlined CPU.
|
||||
*/
|
||||
void clear_tasks_mm_cpumask(int cpu)
|
||||
{
|
||||
struct task_struct *p;
|
||||
|
||||
/*
|
||||
* This function is called after the cpu is taken down and marked
|
||||
* offline, so its not like new tasks will ever get this cpu set in
|
||||
* their mm mask. -- Peter Zijlstra
|
||||
* Thus, we may use rcu_read_lock() here, instead of grabbing
|
||||
* full-fledged tasklist_lock.
|
||||
*/
|
||||
WARN_ON(cpu_online(cpu));
|
||||
rcu_read_lock();
|
||||
for_each_process(p) {
|
||||
struct task_struct *t;
|
||||
|
||||
/*
|
||||
* Main thread might exit, but other threads may still have
|
||||
* a valid mm. Find one.
|
||||
*/
|
||||
t = find_lock_task_mm(p);
|
||||
if (!t)
|
||||
continue;
|
||||
cpumask_clear_cpu(cpu, mm_cpumask(t->mm));
|
||||
task_unlock(t);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static inline void check_for_tasks(int cpu)
|
||||
{
|
||||
struct task_struct *p;
|
||||
|
||||
write_lock_irq(&tasklist_lock);
|
||||
for_each_process(p) {
|
||||
if (task_cpu(p) == cpu && p->state == TASK_RUNNING &&
|
||||
(p->utime || p->stime))
|
||||
printk(KERN_WARNING "Task %s (pid = %d) is on cpu %d "
|
||||
"(state = %ld, flags = %x)\n",
|
||||
p->comm, task_pid_nr(p), cpu,
|
||||
p->state, p->flags);
|
||||
}
|
||||
write_unlock_irq(&tasklist_lock);
|
||||
}
|
||||
|
||||
struct take_cpu_down_param {
|
||||
unsigned long mod;
|
||||
void *hcpu;
|
||||
};
|
||||
|
||||
/* Take this CPU down. */
|
||||
static int __ref take_cpu_down(void *_param)
|
||||
{
|
||||
struct take_cpu_down_param *param = _param;
|
||||
int err;
|
||||
|
||||
/* Ensure this CPU doesn't handle any more interrupts. */
|
||||
err = __cpu_disable();
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
cpu_notify(CPU_DYING | param->mod, param->hcpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Requires cpu_add_remove_lock to be held */
|
||||
static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
|
||||
{
|
||||
int err, nr_calls = 0;
|
||||
void *hcpu = (void *)(long)cpu;
|
||||
unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0;
|
||||
struct take_cpu_down_param tcd_param = {
|
||||
.mod = mod,
|
||||
.hcpu = hcpu,
|
||||
};
|
||||
|
||||
if (num_online_cpus() == 1)
|
||||
return -EBUSY;
|
||||
|
||||
if (!cpu_online(cpu))
|
||||
return -EINVAL;
|
||||
|
||||
cpu_hotplug_begin();
|
||||
|
||||
err = __cpu_notify(CPU_DOWN_PREPARE | mod, hcpu, -1, &nr_calls);
|
||||
if (err) {
|
||||
nr_calls--;
|
||||
__cpu_notify(CPU_DOWN_FAILED | mod, hcpu, nr_calls, NULL);
|
||||
printk("%s: attempt to take down CPU %u failed\n",
|
||||
__func__, cpu);
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu));
|
||||
if (err) {
|
||||
/* CPU didn't die: tell everyone. Can't complain. */
|
||||
cpu_notify_nofail(CPU_DOWN_FAILED | mod, hcpu);
|
||||
|
||||
goto out_release;
|
||||
}
|
||||
BUG_ON(cpu_online(cpu));
|
||||
|
||||
/*
|
||||
* The migration_call() CPU_DYING callback will have removed all
|
||||
* runnable tasks from the cpu, there's only the idle task left now
|
||||
* that the migration thread is done doing the stop_machine thing.
|
||||
*
|
||||
* Wait for the stop thread to go away.
|
||||
*/
|
||||
while (!idle_cpu(cpu))
|
||||
cpu_relax();
|
||||
|
||||
/* This actually kills the CPU. */
|
||||
__cpu_die(cpu);
|
||||
|
||||
/* CPU is completely dead: tell everyone. Too late to complain. */
|
||||
cpu_notify_nofail(CPU_DEAD | mod, hcpu);
|
||||
|
||||
check_for_tasks(cpu);
|
||||
|
||||
out_release:
|
||||
cpu_hotplug_done();
|
||||
if (!err)
|
||||
cpu_notify_nofail(CPU_POST_DEAD | mod, hcpu);
|
||||
return err;
|
||||
}
|
||||
|
||||
int __ref cpu_down(unsigned int cpu)
|
||||
{
|
||||
int err;
|
||||
|
||||
cpu_maps_update_begin();
|
||||
|
||||
if (cpu_hotplug_disabled) {
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = _cpu_down(cpu, 0);
|
||||
|
||||
out:
|
||||
cpu_maps_update_done();
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(cpu_down);
|
||||
#endif /*CONFIG_HOTPLUG_CPU*/
|
||||
|
||||
/* Requires cpu_add_remove_lock to be held */
|
||||
static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen)
|
||||
{
|
||||
int ret, nr_calls = 0;
|
||||
void *hcpu = (void *)(long)cpu;
|
||||
unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0;
|
||||
struct task_struct *idle;
|
||||
|
||||
if (cpu_online(cpu) || !cpu_present(cpu))
|
||||
return -EINVAL;
|
||||
|
||||
cpu_hotplug_begin();
|
||||
|
||||
idle = idle_thread_get(cpu);
|
||||
if (IS_ERR(idle)) {
|
||||
ret = PTR_ERR(idle);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = __cpu_notify(CPU_UP_PREPARE | mod, hcpu, -1, &nr_calls);
|
||||
if (ret) {
|
||||
nr_calls--;
|
||||
printk(KERN_WARNING "%s: attempt to bring up CPU %u failed\n",
|
||||
__func__, cpu);
|
||||
goto out_notify;
|
||||
}
|
||||
|
||||
/* Arch-specific enabling code. */
|
||||
ret = __cpu_up(cpu, idle);
|
||||
if (ret != 0)
|
||||
goto out_notify;
|
||||
BUG_ON(!cpu_online(cpu));
|
||||
|
||||
/* Now call notifier in preparation. */
|
||||
cpu_notify(CPU_ONLINE | mod, hcpu);
|
||||
|
||||
out_notify:
|
||||
if (ret != 0)
|
||||
__cpu_notify(CPU_UP_CANCELED | mod, hcpu, nr_calls, NULL);
|
||||
out:
|
||||
cpu_hotplug_done();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __cpuinit cpu_up(unsigned int cpu)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
int nid;
|
||||
pg_data_t *pgdat;
|
||||
#endif
|
||||
|
||||
if (!cpu_possible(cpu)) {
|
||||
printk(KERN_ERR "can't online cpu %d because it is not "
|
||||
"configured as may-hotadd at boot time\n", cpu);
|
||||
#if defined(CONFIG_IA64)
|
||||
printk(KERN_ERR "please check additional_cpus= boot "
|
||||
"parameter\n");
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
nid = cpu_to_node(cpu);
|
||||
if (!node_online(nid)) {
|
||||
err = mem_online_node(nid);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
pgdat = NODE_DATA(nid);
|
||||
if (!pgdat) {
|
||||
printk(KERN_ERR
|
||||
"Can't online cpu %d due to NULL pgdat\n", cpu);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (pgdat->node_zonelists->_zonerefs->zone == NULL) {
|
||||
mutex_lock(&zonelists_mutex);
|
||||
build_all_zonelists(NULL);
|
||||
mutex_unlock(&zonelists_mutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
cpu_maps_update_begin();
|
||||
|
||||
if (cpu_hotplug_disabled) {
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = _cpu_up(cpu, 0);
|
||||
|
||||
out:
|
||||
cpu_maps_update_done();
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpu_up);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP_SMP
|
||||
static cpumask_var_t frozen_cpus;
|
||||
|
||||
void __weak arch_disable_nonboot_cpus_begin(void)
|
||||
{
|
||||
}
|
||||
|
||||
void __weak arch_disable_nonboot_cpus_end(void)
|
||||
{
|
||||
}
|
||||
|
||||
int disable_nonboot_cpus(void)
|
||||
{
|
||||
int cpu, first_cpu, error = 0;
|
||||
|
||||
cpu_maps_update_begin();
|
||||
first_cpu = cpumask_first(cpu_online_mask);
|
||||
/*
|
||||
* We take down all of the non-boot CPUs in one shot to avoid races
|
||||
* with the userspace trying to use the CPU hotplug at the same time
|
||||
*/
|
||||
cpumask_clear(frozen_cpus);
|
||||
arch_disable_nonboot_cpus_begin();
|
||||
|
||||
printk("Disabling non-boot CPUs ...\n");
|
||||
for_each_online_cpu(cpu) {
|
||||
if (cpu == first_cpu)
|
||||
continue;
|
||||
error = _cpu_down(cpu, 1);
|
||||
if (!error)
|
||||
cpumask_set_cpu(cpu, frozen_cpus);
|
||||
else {
|
||||
printk(KERN_ERR "Error taking CPU%d down: %d\n",
|
||||
cpu, error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
arch_disable_nonboot_cpus_end();
|
||||
|
||||
if (!error) {
|
||||
BUG_ON(num_online_cpus() > 1);
|
||||
/* Make sure the CPUs won't be enabled by someone else */
|
||||
cpu_hotplug_disabled = 1;
|
||||
} else {
|
||||
printk(KERN_ERR "Non-boot CPUs are not disabled\n");
|
||||
}
|
||||
cpu_maps_update_done();
|
||||
return error;
|
||||
}
|
||||
|
||||
void __weak arch_enable_nonboot_cpus_begin(void)
|
||||
{
|
||||
}
|
||||
|
||||
void __weak arch_enable_nonboot_cpus_end(void)
|
||||
{
|
||||
}
|
||||
|
||||
void __ref enable_nonboot_cpus(void)
|
||||
{
|
||||
int cpu, error;
|
||||
|
||||
/* Allow everyone to use the CPU hotplug again */
|
||||
cpu_maps_update_begin();
|
||||
cpu_hotplug_disabled = 0;
|
||||
if (cpumask_empty(frozen_cpus))
|
||||
goto out;
|
||||
|
||||
printk(KERN_INFO "Enabling non-boot CPUs ...\n");
|
||||
|
||||
arch_enable_nonboot_cpus_begin();
|
||||
|
||||
for_each_cpu(cpu, frozen_cpus) {
|
||||
error = _cpu_up(cpu, 1);
|
||||
if (!error) {
|
||||
printk(KERN_INFO "CPU%d is up\n", cpu);
|
||||
continue;
|
||||
}
|
||||
printk(KERN_WARNING "Error taking CPU%d up: %d\n", cpu, error);
|
||||
}
|
||||
|
||||
arch_enable_nonboot_cpus_end();
|
||||
|
||||
cpumask_clear(frozen_cpus);
|
||||
out:
|
||||
cpu_maps_update_done();
|
||||
}
|
||||
|
||||
static int __init alloc_frozen_cpus(void)
|
||||
{
|
||||
if (!alloc_cpumask_var(&frozen_cpus, GFP_KERNEL|__GFP_ZERO))
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
core_initcall(alloc_frozen_cpus);
|
||||
|
||||
/*
|
||||
* Prevent regular CPU hotplug from racing with the freezer, by disabling CPU
|
||||
* hotplug when tasks are about to be frozen. Also, don't allow the freezer
|
||||
* to continue until any currently running CPU hotplug operation gets
|
||||
* completed.
|
||||
* To modify the 'cpu_hotplug_disabled' flag, we need to acquire the
|
||||
* 'cpu_add_remove_lock'. And this same lock is also taken by the regular
|
||||
* CPU hotplug path and released only after it is complete. Thus, we
|
||||
* (and hence the freezer) will block here until any currently running CPU
|
||||
* hotplug operation gets completed.
|
||||
*/
|
||||
void cpu_hotplug_disable_before_freeze(void)
|
||||
{
|
||||
cpu_maps_update_begin();
|
||||
cpu_hotplug_disabled = 1;
|
||||
cpu_maps_update_done();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* When tasks have been thawed, re-enable regular CPU hotplug (which had been
|
||||
* disabled while beginning to freeze tasks).
|
||||
*/
|
||||
void cpu_hotplug_enable_after_thaw(void)
|
||||
{
|
||||
cpu_maps_update_begin();
|
||||
cpu_hotplug_disabled = 0;
|
||||
cpu_maps_update_done();
|
||||
}
|
||||
|
||||
/*
|
||||
* When callbacks for CPU hotplug notifications are being executed, we must
|
||||
* ensure that the state of the system with respect to the tasks being frozen
|
||||
* or not, as reported by the notification, remains unchanged *throughout the
|
||||
* duration* of the execution of the callbacks.
|
||||
* Hence we need to prevent the freezer from racing with regular CPU hotplug.
|
||||
*
|
||||
* This synchronization is implemented by mutually excluding regular CPU
|
||||
* hotplug and Suspend/Hibernate call paths by hooking onto the Suspend/
|
||||
* Hibernate notifications.
|
||||
*/
|
||||
static int
|
||||
cpu_hotplug_pm_callback(struct notifier_block *nb,
|
||||
unsigned long action, void *ptr)
|
||||
{
|
||||
switch (action) {
|
||||
|
||||
case PM_SUSPEND_PREPARE:
|
||||
case PM_HIBERNATION_PREPARE:
|
||||
cpu_hotplug_disable_before_freeze();
|
||||
break;
|
||||
|
||||
case PM_POST_SUSPEND:
|
||||
case PM_POST_HIBERNATION:
|
||||
cpu_hotplug_enable_after_thaw();
|
||||
break;
|
||||
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
|
||||
static int __init cpu_hotplug_pm_sync_init(void)
|
||||
{
|
||||
pm_notifier(cpu_hotplug_pm_callback, 0);
|
||||
return 0;
|
||||
}
|
||||
core_initcall(cpu_hotplug_pm_sync_init);
|
||||
|
||||
#endif /* CONFIG_PM_SLEEP_SMP */
|
||||
|
||||
/**
|
||||
* notify_cpu_starting(cpu) - call the CPU_STARTING notifiers
|
||||
* @cpu: cpu that just started
|
||||
*
|
||||
* This function calls the cpu_chain notifiers with CPU_STARTING.
|
||||
* It must be called by the arch code on the new cpu, before the new cpu
|
||||
* enables interrupts and before the "boot" cpu returns from __cpu_up().
|
||||
*/
|
||||
void __cpuinit notify_cpu_starting(unsigned int cpu)
|
||||
{
|
||||
unsigned long val = CPU_STARTING;
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP_SMP
|
||||
if (frozen_cpus != NULL && cpumask_test_cpu(cpu, frozen_cpus))
|
||||
val = CPU_STARTING_FROZEN;
|
||||
#endif /* CONFIG_PM_SLEEP_SMP */
|
||||
cpu_notify(val, (void *)(long)cpu);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
/*
|
||||
* cpu_bit_bitmap[] is a special, "compressed" data structure that
|
||||
* represents all NR_CPUS bits binary values of 1<<nr.
|
||||
*
|
||||
* It is used by cpumask_of() to get a constant address to a CPU
|
||||
* mask value that has a single bit set only.
|
||||
*/
|
||||
|
||||
/* cpu_bit_bitmap[0] is empty - so we can back into it */
|
||||
#define MASK_DECLARE_1(x) [x+1][0] = (1UL << (x))
|
||||
#define MASK_DECLARE_2(x) MASK_DECLARE_1(x), MASK_DECLARE_1(x+1)
|
||||
#define MASK_DECLARE_4(x) MASK_DECLARE_2(x), MASK_DECLARE_2(x+2)
|
||||
#define MASK_DECLARE_8(x) MASK_DECLARE_4(x), MASK_DECLARE_4(x+4)
|
||||
|
||||
const unsigned long cpu_bit_bitmap[BITS_PER_LONG+1][BITS_TO_LONGS(NR_CPUS)] = {
|
||||
|
||||
MASK_DECLARE_8(0), MASK_DECLARE_8(8),
|
||||
MASK_DECLARE_8(16), MASK_DECLARE_8(24),
|
||||
#if BITS_PER_LONG > 32
|
||||
MASK_DECLARE_8(32), MASK_DECLARE_8(40),
|
||||
MASK_DECLARE_8(48), MASK_DECLARE_8(56),
|
||||
#endif
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(cpu_bit_bitmap);
|
||||
|
||||
const DECLARE_BITMAP(cpu_all_bits, NR_CPUS) = CPU_BITS_ALL;
|
||||
EXPORT_SYMBOL(cpu_all_bits);
|
||||
|
||||
#ifdef CONFIG_INIT_ALL_POSSIBLE
|
||||
static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly
|
||||
= CPU_BITS_ALL;
|
||||
#else
|
||||
static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly;
|
||||
#endif
|
||||
const struct cpumask *const cpu_possible_mask = to_cpumask(cpu_possible_bits);
|
||||
EXPORT_SYMBOL(cpu_possible_mask);
|
||||
|
||||
static DECLARE_BITMAP(cpu_online_bits, CONFIG_NR_CPUS) __read_mostly;
|
||||
const struct cpumask *const cpu_online_mask = to_cpumask(cpu_online_bits);
|
||||
EXPORT_SYMBOL(cpu_online_mask);
|
||||
|
||||
static DECLARE_BITMAP(cpu_present_bits, CONFIG_NR_CPUS) __read_mostly;
|
||||
const struct cpumask *const cpu_present_mask = to_cpumask(cpu_present_bits);
|
||||
EXPORT_SYMBOL(cpu_present_mask);
|
||||
|
||||
static DECLARE_BITMAP(cpu_active_bits, CONFIG_NR_CPUS) __read_mostly;
|
||||
const struct cpumask *const cpu_active_mask = to_cpumask(cpu_active_bits);
|
||||
EXPORT_SYMBOL(cpu_active_mask);
|
||||
|
||||
void set_cpu_possible(unsigned int cpu, bool possible)
|
||||
{
|
||||
if (possible)
|
||||
cpumask_set_cpu(cpu, to_cpumask(cpu_possible_bits));
|
||||
else
|
||||
cpumask_clear_cpu(cpu, to_cpumask(cpu_possible_bits));
|
||||
}
|
||||
|
||||
void set_cpu_present(unsigned int cpu, bool present)
|
||||
{
|
||||
if (present)
|
||||
cpumask_set_cpu(cpu, to_cpumask(cpu_present_bits));
|
||||
else
|
||||
cpumask_clear_cpu(cpu, to_cpumask(cpu_present_bits));
|
||||
}
|
||||
|
||||
void set_cpu_online(unsigned int cpu, bool online)
|
||||
{
|
||||
if (online)
|
||||
cpumask_set_cpu(cpu, to_cpumask(cpu_online_bits));
|
||||
else
|
||||
cpumask_clear_cpu(cpu, to_cpumask(cpu_online_bits));
|
||||
}
|
||||
|
||||
void set_cpu_active(unsigned int cpu, bool active)
|
||||
{
|
||||
if (active)
|
||||
cpumask_set_cpu(cpu, to_cpumask(cpu_active_bits));
|
||||
else
|
||||
cpumask_clear_cpu(cpu, to_cpumask(cpu_active_bits));
|
||||
}
|
||||
|
||||
void init_cpu_present(const struct cpumask *src)
|
||||
{
|
||||
cpumask_copy(to_cpumask(cpu_present_bits), src);
|
||||
}
|
||||
|
||||
void init_cpu_possible(const struct cpumask *src)
|
||||
{
|
||||
cpumask_copy(to_cpumask(cpu_possible_bits), src);
|
||||
}
|
||||
|
||||
void init_cpu_online(const struct cpumask *src)
|
||||
{
|
||||
cpumask_copy(to_cpumask(cpu_online_bits), src);
|
||||
}
|
||||
784
samples/C/diff.c
Normal file
784
samples/C/diff.c
Normal file
@@ -0,0 +1,784 @@
|
||||
/*
|
||||
* Copyright (C) 2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
#include "common.h"
|
||||
#include "git2/diff.h"
|
||||
#include "diff.h"
|
||||
#include "fileops.h"
|
||||
#include "config.h"
|
||||
#include "attr_file.h"
|
||||
|
||||
static char *diff_prefix_from_pathspec(const git_strarray *pathspec)
|
||||
{
|
||||
git_buf prefix = GIT_BUF_INIT;
|
||||
const char *scan;
|
||||
|
||||
if (git_buf_common_prefix(&prefix, pathspec) < 0)
|
||||
return NULL;
|
||||
|
||||
/* diff prefix will only be leading non-wildcards */
|
||||
for (scan = prefix.ptr; *scan && !git__iswildcard(*scan); ++scan);
|
||||
git_buf_truncate(&prefix, scan - prefix.ptr);
|
||||
|
||||
if (prefix.size > 0)
|
||||
return git_buf_detach(&prefix);
|
||||
|
||||
git_buf_free(&prefix);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool diff_pathspec_is_interesting(const git_strarray *pathspec)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
if (pathspec == NULL || pathspec->count == 0)
|
||||
return false;
|
||||
if (pathspec->count > 1)
|
||||
return true;
|
||||
|
||||
str = pathspec->strings[0];
|
||||
if (!str || !str[0] || (!str[1] && (str[0] == '*' || str[0] == '.')))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool diff_path_matches_pathspec(git_diff_list *diff, const char *path)
|
||||
{
|
||||
unsigned int i;
|
||||
git_attr_fnmatch *match;
|
||||
|
||||
if (!diff->pathspec.length)
|
||||
return true;
|
||||
|
||||
git_vector_foreach(&diff->pathspec, i, match) {
|
||||
int result = p_fnmatch(match->pattern, path, 0);
|
||||
|
||||
/* if we didn't match, look for exact dirname prefix match */
|
||||
if (result == FNM_NOMATCH &&
|
||||
(match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
|
||||
strncmp(path, match->pattern, match->length) == 0 &&
|
||||
path[match->length] == '/')
|
||||
result = 0;
|
||||
|
||||
if (result == 0)
|
||||
return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static git_diff_delta *diff_delta__alloc(
|
||||
git_diff_list *diff,
|
||||
git_delta_t status,
|
||||
const char *path)
|
||||
{
|
||||
git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta));
|
||||
if (!delta)
|
||||
return NULL;
|
||||
|
||||
delta->old_file.path = git_pool_strdup(&diff->pool, path);
|
||||
if (delta->old_file.path == NULL) {
|
||||
git__free(delta);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
delta->new_file.path = delta->old_file.path;
|
||||
|
||||
if (diff->opts.flags & GIT_DIFF_REVERSE) {
|
||||
switch (status) {
|
||||
case GIT_DELTA_ADDED: status = GIT_DELTA_DELETED; break;
|
||||
case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break;
|
||||
default: break; /* leave other status values alone */
|
||||
}
|
||||
}
|
||||
delta->status = status;
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
static git_diff_delta *diff_delta__dup(
|
||||
const git_diff_delta *d, git_pool *pool)
|
||||
{
|
||||
git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
|
||||
if (!delta)
|
||||
return NULL;
|
||||
|
||||
memcpy(delta, d, sizeof(git_diff_delta));
|
||||
|
||||
delta->old_file.path = git_pool_strdup(pool, d->old_file.path);
|
||||
if (delta->old_file.path == NULL)
|
||||
goto fail;
|
||||
|
||||
if (d->new_file.path != d->old_file.path) {
|
||||
delta->new_file.path = git_pool_strdup(pool, d->new_file.path);
|
||||
if (delta->new_file.path == NULL)
|
||||
goto fail;
|
||||
} else {
|
||||
delta->new_file.path = delta->old_file.path;
|
||||
}
|
||||
|
||||
return delta;
|
||||
|
||||
fail:
|
||||
git__free(delta);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static git_diff_delta *diff_delta__merge_like_cgit(
|
||||
const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
|
||||
{
|
||||
git_diff_delta *dup = diff_delta__dup(a, pool);
|
||||
if (!dup)
|
||||
return NULL;
|
||||
|
||||
if (git_oid_cmp(&dup->new_file.oid, &b->new_file.oid) == 0)
|
||||
return dup;
|
||||
|
||||
git_oid_cpy(&dup->new_file.oid, &b->new_file.oid);
|
||||
|
||||
dup->new_file.mode = b->new_file.mode;
|
||||
dup->new_file.size = b->new_file.size;
|
||||
dup->new_file.flags = b->new_file.flags;
|
||||
|
||||
/* Emulate C git for merging two diffs (a la 'git diff <sha>').
|
||||
*
|
||||
* When C git does a diff between the work dir and a tree, it actually
|
||||
* diffs with the index but uses the workdir contents. This emulates
|
||||
* those choices so we can emulate the type of diff.
|
||||
*/
|
||||
if (git_oid_cmp(&dup->old_file.oid, &dup->new_file.oid) == 0) {
|
||||
if (dup->status == GIT_DELTA_DELETED)
|
||||
/* preserve pending delete info */;
|
||||
else if (b->status == GIT_DELTA_UNTRACKED ||
|
||||
b->status == GIT_DELTA_IGNORED)
|
||||
dup->status = b->status;
|
||||
else
|
||||
dup->status = GIT_DELTA_UNMODIFIED;
|
||||
}
|
||||
else if (dup->status == GIT_DELTA_UNMODIFIED ||
|
||||
b->status == GIT_DELTA_DELETED)
|
||||
dup->status = b->status;
|
||||
|
||||
return dup;
|
||||
}
|
||||
|
||||
static int diff_delta__from_one(
|
||||
git_diff_list *diff,
|
||||
git_delta_t status,
|
||||
const git_index_entry *entry)
|
||||
{
|
||||
git_diff_delta *delta;
|
||||
|
||||
if (status == GIT_DELTA_IGNORED &&
|
||||
(diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
|
||||
return 0;
|
||||
|
||||
if (status == GIT_DELTA_UNTRACKED &&
|
||||
(diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
|
||||
return 0;
|
||||
|
||||
if (!diff_path_matches_pathspec(diff, entry->path))
|
||||
return 0;
|
||||
|
||||
delta = diff_delta__alloc(diff, status, entry->path);
|
||||
GITERR_CHECK_ALLOC(delta);
|
||||
|
||||
/* This fn is just for single-sided diffs */
|
||||
assert(status != GIT_DELTA_MODIFIED);
|
||||
|
||||
if (delta->status == GIT_DELTA_DELETED) {
|
||||
delta->old_file.mode = entry->mode;
|
||||
delta->old_file.size = entry->file_size;
|
||||
git_oid_cpy(&delta->old_file.oid, &entry->oid);
|
||||
} else /* ADDED, IGNORED, UNTRACKED */ {
|
||||
delta->new_file.mode = entry->mode;
|
||||
delta->new_file.size = entry->file_size;
|
||||
git_oid_cpy(&delta->new_file.oid, &entry->oid);
|
||||
}
|
||||
|
||||
delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
|
||||
delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
|
||||
|
||||
if (git_vector_insert(&diff->deltas, delta) < 0) {
|
||||
git__free(delta);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diff_delta__from_two(
|
||||
git_diff_list *diff,
|
||||
git_delta_t status,
|
||||
const git_index_entry *old_entry,
|
||||
const git_index_entry *new_entry,
|
||||
git_oid *new_oid)
|
||||
{
|
||||
git_diff_delta *delta;
|
||||
|
||||
if (status == GIT_DELTA_UNMODIFIED &&
|
||||
(diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
|
||||
return 0;
|
||||
|
||||
if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {
|
||||
const git_index_entry *temp = old_entry;
|
||||
old_entry = new_entry;
|
||||
new_entry = temp;
|
||||
}
|
||||
|
||||
delta = diff_delta__alloc(diff, status, old_entry->path);
|
||||
GITERR_CHECK_ALLOC(delta);
|
||||
|
||||
delta->old_file.mode = old_entry->mode;
|
||||
git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
|
||||
delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
|
||||
|
||||
delta->new_file.mode = new_entry->mode;
|
||||
git_oid_cpy(&delta->new_file.oid, new_oid ? new_oid : &new_entry->oid);
|
||||
if (new_oid || !git_oid_iszero(&new_entry->oid))
|
||||
delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
|
||||
|
||||
if (git_vector_insert(&diff->deltas, delta) < 0) {
|
||||
git__free(delta);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
|
||||
{
|
||||
size_t len = strlen(prefix);
|
||||
|
||||
/* append '/' at end if needed */
|
||||
if (len > 0 && prefix[len - 1] != '/')
|
||||
return git_pool_strcat(pool, prefix, "/");
|
||||
else
|
||||
return git_pool_strndup(pool, prefix, len + 1);
|
||||
}
|
||||
|
||||
static int diff_delta__cmp(const void *a, const void *b)
|
||||
{
|
||||
const git_diff_delta *da = a, *db = b;
|
||||
int val = strcmp(da->old_file.path, db->old_file.path);
|
||||
return val ? val : ((int)da->status - (int)db->status);
|
||||
}
|
||||
|
||||
static int config_bool(git_config *cfg, const char *name, int defvalue)
|
||||
{
|
||||
int val = defvalue;
|
||||
|
||||
if (git_config_get_bool(&val, cfg, name) < 0)
|
||||
giterr_clear();
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static git_diff_list *git_diff_list_alloc(
|
||||
git_repository *repo, const git_diff_options *opts)
|
||||
{
|
||||
git_config *cfg;
|
||||
size_t i;
|
||||
git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
|
||||
if (diff == NULL)
|
||||
return NULL;
|
||||
|
||||
diff->repo = repo;
|
||||
|
||||
if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 ||
|
||||
git_pool_init(&diff->pool, 1, 0) < 0)
|
||||
goto fail;
|
||||
|
||||
/* load config values that affect diff behavior */
|
||||
if (git_repository_config__weakptr(&cfg, repo) < 0)
|
||||
goto fail;
|
||||
if (config_bool(cfg, "core.symlinks", 1))
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
|
||||
if (config_bool(cfg, "core.ignorestat", 0))
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;
|
||||
if (config_bool(cfg, "core.filemode", 1))
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_EXEC_BIT;
|
||||
if (config_bool(cfg, "core.trustctime", 1))
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
|
||||
/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
|
||||
|
||||
if (opts == NULL)
|
||||
return diff;
|
||||
|
||||
memcpy(&diff->opts, opts, sizeof(git_diff_options));
|
||||
memset(&diff->opts.pathspec, 0, sizeof(diff->opts.pathspec));
|
||||
|
||||
diff->opts.old_prefix = diff_strdup_prefix(&diff->pool,
|
||||
opts->old_prefix ? opts->old_prefix : DIFF_OLD_PREFIX_DEFAULT);
|
||||
diff->opts.new_prefix = diff_strdup_prefix(&diff->pool,
|
||||
opts->new_prefix ? opts->new_prefix : DIFF_NEW_PREFIX_DEFAULT);
|
||||
|
||||
if (!diff->opts.old_prefix || !diff->opts.new_prefix)
|
||||
goto fail;
|
||||
|
||||
if (diff->opts.flags & GIT_DIFF_REVERSE) {
|
||||
char *swap = diff->opts.old_prefix;
|
||||
diff->opts.old_prefix = diff->opts.new_prefix;
|
||||
diff->opts.new_prefix = swap;
|
||||
}
|
||||
|
||||
/* only copy pathspec if it is "interesting" so we can test
|
||||
* diff->pathspec.length > 0 to know if it is worth calling
|
||||
* fnmatch as we iterate.
|
||||
*/
|
||||
if (!diff_pathspec_is_interesting(&opts->pathspec))
|
||||
return diff;
|
||||
|
||||
if (git_vector_init(
|
||||
&diff->pathspec, (unsigned int)opts->pathspec.count, NULL) < 0)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < opts->pathspec.count; ++i) {
|
||||
int ret;
|
||||
const char *pattern = opts->pathspec.strings[i];
|
||||
git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
|
||||
if (!match)
|
||||
goto fail;
|
||||
match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
|
||||
ret = git_attr_fnmatch__parse(match, &diff->pool, NULL, &pattern);
|
||||
if (ret == GIT_ENOTFOUND) {
|
||||
git__free(match);
|
||||
continue;
|
||||
} else if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
if (git_vector_insert(&diff->pathspec, match) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return diff;
|
||||
|
||||
fail:
|
||||
git_diff_list_free(diff);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void git_diff_list_free(git_diff_list *diff)
|
||||
{
|
||||
git_diff_delta *delta;
|
||||
git_attr_fnmatch *match;
|
||||
unsigned int i;
|
||||
|
||||
if (!diff)
|
||||
return;
|
||||
|
||||
git_vector_foreach(&diff->deltas, i, delta) {
|
||||
git__free(delta);
|
||||
diff->deltas.contents[i] = NULL;
|
||||
}
|
||||
git_vector_free(&diff->deltas);
|
||||
|
||||
git_vector_foreach(&diff->pathspec, i, match) {
|
||||
git__free(match);
|
||||
diff->pathspec.contents[i] = NULL;
|
||||
}
|
||||
git_vector_free(&diff->pathspec);
|
||||
|
||||
git_pool_clear(&diff->pool);
|
||||
git__free(diff);
|
||||
}
|
||||
|
||||
static int oid_for_workdir_item(
|
||||
git_repository *repo,
|
||||
const git_index_entry *item,
|
||||
git_oid *oid)
|
||||
{
|
||||
int result;
|
||||
git_buf full_path = GIT_BUF_INIT;
|
||||
|
||||
if (git_buf_joinpath(&full_path, git_repository_workdir(repo), item->path) < 0)
|
||||
return -1;
|
||||
|
||||
/* calculate OID for file if possible*/
|
||||
if (S_ISLNK(item->mode))
|
||||
result = git_odb__hashlink(oid, full_path.ptr);
|
||||
else if (!git__is_sizet(item->file_size)) {
|
||||
giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
|
||||
result = -1;
|
||||
} else {
|
||||
int fd = git_futils_open_ro(full_path.ptr);
|
||||
if (fd < 0)
|
||||
result = fd;
|
||||
else {
|
||||
result = git_odb__hashfd(
|
||||
oid, fd, (size_t)item->file_size, GIT_OBJ_BLOB);
|
||||
p_close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
git_buf_free(&full_path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define EXEC_BIT_MASK 0000111
|
||||
|
||||
static int maybe_modified(
|
||||
git_iterator *old_iter,
|
||||
const git_index_entry *oitem,
|
||||
git_iterator *new_iter,
|
||||
const git_index_entry *nitem,
|
||||
git_diff_list *diff)
|
||||
{
|
||||
git_oid noid, *use_noid = NULL;
|
||||
git_delta_t status = GIT_DELTA_MODIFIED;
|
||||
unsigned int omode = oitem->mode;
|
||||
unsigned int nmode = nitem->mode;
|
||||
|
||||
GIT_UNUSED(old_iter);
|
||||
|
||||
if (!diff_path_matches_pathspec(diff, oitem->path))
|
||||
return 0;
|
||||
|
||||
/* on platforms with no symlinks, promote plain files to symlinks */
|
||||
if (S_ISLNK(omode) && S_ISREG(nmode) &&
|
||||
!(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
|
||||
nmode = GIT_MODE_TYPE(omode) | (nmode & GIT_MODE_PERMS_MASK);
|
||||
|
||||
/* on platforms with no execmode, clear exec bit from comparisons */
|
||||
if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_EXEC_BIT)) {
|
||||
omode = omode & ~EXEC_BIT_MASK;
|
||||
nmode = nmode & ~EXEC_BIT_MASK;
|
||||
}
|
||||
|
||||
/* support "assume unchanged" (badly, b/c we still stat everything) */
|
||||
if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0)
|
||||
status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ?
|
||||
GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED;
|
||||
|
||||
/* support "skip worktree" index bit */
|
||||
else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
/* if basic type of file changed, then split into delete and add */
|
||||
else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
|
||||
if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
|
||||
diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if oids and modes match, then file is unmodified */
|
||||
else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
|
||||
omode == nmode)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
/* if we have a workdir item with an unknown oid, check deeper */
|
||||
else if (git_oid_iszero(&nitem->oid) && new_iter->type == GIT_ITERATOR_WORKDIR) {
|
||||
/* TODO: add check against index file st_mtime to avoid racy-git */
|
||||
|
||||
/* if they files look exactly alike, then we'll assume the same */
|
||||
if (oitem->file_size == nitem->file_size &&
|
||||
(!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) ||
|
||||
(oitem->ctime.seconds == nitem->ctime.seconds)) &&
|
||||
oitem->mtime.seconds == nitem->mtime.seconds &&
|
||||
(!(diff->diffcaps & GIT_DIFFCAPS_USE_DEV) ||
|
||||
(oitem->dev == nitem->dev)) &&
|
||||
oitem->ino == nitem->ino &&
|
||||
oitem->uid == nitem->uid &&
|
||||
oitem->gid == nitem->gid)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
else if (S_ISGITLINK(nmode)) {
|
||||
git_submodule *sub;
|
||||
|
||||
if ((diff->opts.flags & GIT_DIFF_IGNORE_SUBMODULES) != 0)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0)
|
||||
return -1;
|
||||
else if (sub->ignore == GIT_SUBMODULE_IGNORE_ALL)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
else {
|
||||
/* TODO: support other GIT_SUBMODULE_IGNORE values */
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: check git attributes so we will not have to read the file
|
||||
* in if it is marked binary.
|
||||
*/
|
||||
|
||||
else if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
|
||||
return -1;
|
||||
|
||||
else if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
|
||||
omode == nmode)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
/* store calculated oid so we don't have to recalc later */
|
||||
use_noid = &noid;
|
||||
}
|
||||
|
||||
return diff_delta__from_two(diff, status, oitem, nitem, use_noid);
|
||||
}
|
||||
|
||||
static int diff_from_iterators(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts, /**< can be NULL for defaults */
|
||||
git_iterator *old_iter,
|
||||
git_iterator *new_iter,
|
||||
git_diff_list **diff_ptr)
|
||||
{
|
||||
const git_index_entry *oitem, *nitem;
|
||||
git_buf ignore_prefix = GIT_BUF_INIT;
|
||||
git_diff_list *diff = git_diff_list_alloc(repo, opts);
|
||||
if (!diff)
|
||||
goto fail;
|
||||
|
||||
diff->old_src = old_iter->type;
|
||||
diff->new_src = new_iter->type;
|
||||
|
||||
if (git_iterator_current(old_iter, &oitem) < 0 ||
|
||||
git_iterator_current(new_iter, &nitem) < 0)
|
||||
goto fail;
|
||||
|
||||
/* run iterators building diffs */
|
||||
while (oitem || nitem) {
|
||||
|
||||
/* create DELETED records for old items not matched in new */
|
||||
if (oitem && (!nitem || strcmp(oitem->path, nitem->path) < 0)) {
|
||||
if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
|
||||
git_iterator_advance(old_iter, &oitem) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* create ADDED, TRACKED, or IGNORED records for new items not
|
||||
* matched in old (and/or descend into directories as needed)
|
||||
*/
|
||||
else if (nitem && (!oitem || strcmp(oitem->path, nitem->path) > 0)) {
|
||||
git_delta_t delta_type = GIT_DELTA_UNTRACKED;
|
||||
|
||||
/* check if contained in ignored parent directory */
|
||||
if (git_buf_len(&ignore_prefix) &&
|
||||
git__prefixcmp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
|
||||
if (S_ISDIR(nitem->mode)) {
|
||||
/* recurse into directory only if there are tracked items in
|
||||
* it or if the user requested the contents of untracked
|
||||
* directories and it is not under an ignored directory.
|
||||
*/
|
||||
if ((oitem && git__prefixcmp(oitem->path, nitem->path) == 0) ||
|
||||
(delta_type == GIT_DELTA_UNTRACKED &&
|
||||
(diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0))
|
||||
{
|
||||
/* if this directory is ignored, remember it as the
|
||||
* "ignore_prefix" for processing contained items
|
||||
*/
|
||||
if (delta_type == GIT_DELTA_UNTRACKED &&
|
||||
git_iterator_current_is_ignored(new_iter))
|
||||
git_buf_sets(&ignore_prefix, nitem->path);
|
||||
|
||||
if (git_iterator_advance_into_directory(new_iter, &nitem) < 0)
|
||||
goto fail;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* In core git, the next two "else if" clauses are effectively
|
||||
* reversed -- i.e. when an untracked file contained in an
|
||||
* ignored directory is individually ignored, it shows up as an
|
||||
* ignored file in the diff list, even though other untracked
|
||||
* files in the same directory are skipped completely.
|
||||
*
|
||||
* To me, this is odd. If the directory is ignored and the file
|
||||
* is untracked, we should skip it consistently, regardless of
|
||||
* whether it happens to match a pattern in the ignore file.
|
||||
*
|
||||
* To match the core git behavior, just reverse the following
|
||||
* two "else if" cases so that individual file ignores are
|
||||
* checked before container directory exclusions are used to
|
||||
* skip the file.
|
||||
*/
|
||||
else if (delta_type == GIT_DELTA_IGNORED) {
|
||||
if (git_iterator_advance(new_iter, &nitem) < 0)
|
||||
goto fail;
|
||||
continue; /* ignored parent directory, so skip completely */
|
||||
}
|
||||
|
||||
else if (git_iterator_current_is_ignored(new_iter))
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
|
||||
else if (new_iter->type != GIT_ITERATOR_WORKDIR)
|
||||
delta_type = GIT_DELTA_ADDED;
|
||||
|
||||
if (diff_delta__from_one(diff, delta_type, nitem) < 0 ||
|
||||
git_iterator_advance(new_iter, &nitem) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* otherwise item paths match, so create MODIFIED record
|
||||
* (or ADDED and DELETED pair if type changed)
|
||||
*/
|
||||
else {
|
||||
assert(oitem && nitem && strcmp(oitem->path, nitem->path) == 0);
|
||||
|
||||
if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
|
||||
git_iterator_advance(old_iter, &oitem) < 0 ||
|
||||
git_iterator_advance(new_iter, &nitem) < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
git_iterator_free(old_iter);
|
||||
git_iterator_free(new_iter);
|
||||
git_buf_free(&ignore_prefix);
|
||||
|
||||
*diff_ptr = diff;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
git_iterator_free(old_iter);
|
||||
git_iterator_free(new_iter);
|
||||
git_buf_free(&ignore_prefix);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
*diff_ptr = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int git_diff_tree_to_tree(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts, /**< can be NULL for defaults */
|
||||
git_tree *old_tree,
|
||||
git_tree *new_tree,
|
||||
git_diff_list **diff)
|
||||
{
|
||||
git_iterator *a = NULL, *b = NULL;
|
||||
char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
|
||||
|
||||
assert(repo && old_tree && new_tree && diff);
|
||||
|
||||
if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
|
||||
git_iterator_for_tree_range(&b, repo, new_tree, prefix, prefix) < 0)
|
||||
return -1;
|
||||
|
||||
git__free(prefix);
|
||||
|
||||
return diff_from_iterators(repo, opts, a, b, diff);
|
||||
}
|
||||
|
||||
int git_diff_index_to_tree(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts,
|
||||
git_tree *old_tree,
|
||||
git_diff_list **diff)
|
||||
{
|
||||
git_iterator *a = NULL, *b = NULL;
|
||||
char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
|
||||
|
||||
assert(repo && diff);
|
||||
|
||||
if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
|
||||
git_iterator_for_index_range(&b, repo, prefix, prefix) < 0)
|
||||
return -1;
|
||||
|
||||
git__free(prefix);
|
||||
|
||||
return diff_from_iterators(repo, opts, a, b, diff);
|
||||
}
|
||||
|
||||
int git_diff_workdir_to_index(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts,
|
||||
git_diff_list **diff)
|
||||
{
|
||||
git_iterator *a = NULL, *b = NULL;
|
||||
char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
|
||||
|
||||
assert(repo && diff);
|
||||
|
||||
if (git_iterator_for_index_range(&a, repo, prefix, prefix) < 0 ||
|
||||
git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
|
||||
return -1;
|
||||
|
||||
git__free(prefix);
|
||||
|
||||
return diff_from_iterators(repo, opts, a, b, diff);
|
||||
}
|
||||
|
||||
|
||||
int git_diff_workdir_to_tree(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts,
|
||||
git_tree *old_tree,
|
||||
git_diff_list **diff)
|
||||
{
|
||||
git_iterator *a = NULL, *b = NULL;
|
||||
char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
|
||||
|
||||
assert(repo && old_tree && diff);
|
||||
|
||||
if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
|
||||
git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
|
||||
return -1;
|
||||
|
||||
git__free(prefix);
|
||||
|
||||
return diff_from_iterators(repo, opts, a, b, diff);
|
||||
}
|
||||
|
||||
int git_diff_merge(
|
||||
git_diff_list *onto,
|
||||
const git_diff_list *from)
|
||||
{
|
||||
int error = 0;
|
||||
git_pool onto_pool;
|
||||
git_vector onto_new;
|
||||
git_diff_delta *delta;
|
||||
unsigned int i, j;
|
||||
|
||||
assert(onto && from);
|
||||
|
||||
if (!from->deltas.length)
|
||||
return 0;
|
||||
|
||||
if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0 ||
|
||||
git_pool_init(&onto_pool, 1, 0) < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
|
||||
git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
|
||||
const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
|
||||
int cmp = !f ? -1 : !o ? 1 : strcmp(o->old_file.path, f->old_file.path);
|
||||
|
||||
if (cmp < 0) {
|
||||
delta = diff_delta__dup(o, &onto_pool);
|
||||
i++;
|
||||
} else if (cmp > 0) {
|
||||
delta = diff_delta__dup(f, &onto_pool);
|
||||
j++;
|
||||
} else {
|
||||
delta = diff_delta__merge_like_cgit(o, f, &onto_pool);
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
|
||||
if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
git_vector_swap(&onto->deltas, &onto_new);
|
||||
git_pool_swap(&onto->pool, &onto_pool);
|
||||
onto->new_src = from->new_src;
|
||||
}
|
||||
|
||||
git_vector_foreach(&onto_new, i, delta)
|
||||
git__free(delta);
|
||||
git_vector_free(&onto_new);
|
||||
git_pool_clear(&onto_pool);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
609
samples/C/git.c
Normal file
609
samples/C/git.c
Normal file
@@ -0,0 +1,609 @@
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "help.h"
|
||||
#include "quote.h"
|
||||
#include "run-command.h"
|
||||
|
||||
const char git_usage_string[] =
|
||||
"git [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
|
||||
" [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]\n"
|
||||
" [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
|
||||
" [-c name=value] [--help]\n"
|
||||
" <command> [<args>]";
|
||||
|
||||
const char git_more_info_string[] =
|
||||
N_("See 'git help <command>' for more information on a specific command.");
|
||||
|
||||
static struct startup_info git_startup_info;
|
||||
static int use_pager = -1;
|
||||
struct pager_config {
|
||||
const char *cmd;
|
||||
int want;
|
||||
char *value;
|
||||
};
|
||||
|
||||
static int pager_command_config(const char *var, const char *value, void *data)
|
||||
{
|
||||
struct pager_config *c = data;
|
||||
if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) {
|
||||
int b = git_config_maybe_bool(var, value);
|
||||
if (b >= 0)
|
||||
c->want = b;
|
||||
else {
|
||||
c->want = 1;
|
||||
c->value = xstrdup(value);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
|
||||
int check_pager_config(const char *cmd)
|
||||
{
|
||||
struct pager_config c;
|
||||
c.cmd = cmd;
|
||||
c.want = -1;
|
||||
c.value = NULL;
|
||||
git_config(pager_command_config, &c);
|
||||
if (c.value)
|
||||
pager_program = c.value;
|
||||
return c.want;
|
||||
}
|
||||
|
||||
static void commit_pager_choice(void) {
|
||||
switch (use_pager) {
|
||||
case 0:
|
||||
setenv("GIT_PAGER", "cat", 1);
|
||||
break;
|
||||
case 1:
|
||||
setup_pager();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_options(const char ***argv, int *argc, int *envchanged)
|
||||
{
|
||||
const char **orig_argv = *argv;
|
||||
|
||||
while (*argc > 0) {
|
||||
const char *cmd = (*argv)[0];
|
||||
if (cmd[0] != '-')
|
||||
break;
|
||||
|
||||
/*
|
||||
* For legacy reasons, the "version" and "help"
|
||||
* commands can be written with "--" prepended
|
||||
* to make them look like flags.
|
||||
*/
|
||||
if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version"))
|
||||
break;
|
||||
|
||||
/*
|
||||
* Check remaining flags.
|
||||
*/
|
||||
if (!prefixcmp(cmd, "--exec-path")) {
|
||||
cmd += 11;
|
||||
if (*cmd == '=')
|
||||
git_set_argv_exec_path(cmd + 1);
|
||||
else {
|
||||
puts(git_exec_path());
|
||||
exit(0);
|
||||
}
|
||||
} else if (!strcmp(cmd, "--html-path")) {
|
||||
puts(system_path(GIT_HTML_PATH));
|
||||
exit(0);
|
||||
} else if (!strcmp(cmd, "--man-path")) {
|
||||
puts(system_path(GIT_MAN_PATH));
|
||||
exit(0);
|
||||
} else if (!strcmp(cmd, "--info-path")) {
|
||||
puts(system_path(GIT_INFO_PATH));
|
||||
exit(0);
|
||||
} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
|
||||
use_pager = 1;
|
||||
} else if (!strcmp(cmd, "--no-pager")) {
|
||||
use_pager = 0;
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
} else if (!strcmp(cmd, "--no-replace-objects")) {
|
||||
read_replace_refs = 0;
|
||||
setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
} else if (!strcmp(cmd, "--git-dir")) {
|
||||
if (*argc < 2) {
|
||||
fprintf(stderr, "No directory given for --git-dir.\n" );
|
||||
usage(git_usage_string);
|
||||
}
|
||||
setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
(*argv)++;
|
||||
(*argc)--;
|
||||
} else if (!prefixcmp(cmd, "--git-dir=")) {
|
||||
setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
} else if (!strcmp(cmd, "--namespace")) {
|
||||
if (*argc < 2) {
|
||||
fprintf(stderr, "No namespace given for --namespace.\n" );
|
||||
usage(git_usage_string);
|
||||
}
|
||||
setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
(*argv)++;
|
||||
(*argc)--;
|
||||
} else if (!prefixcmp(cmd, "--namespace=")) {
|
||||
setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
} else if (!strcmp(cmd, "--work-tree")) {
|
||||
if (*argc < 2) {
|
||||
fprintf(stderr, "No directory given for --work-tree.\n" );
|
||||
usage(git_usage_string);
|
||||
}
|
||||
setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
(*argv)++;
|
||||
(*argc)--;
|
||||
} else if (!prefixcmp(cmd, "--work-tree=")) {
|
||||
setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
} else if (!strcmp(cmd, "--bare")) {
|
||||
static char git_dir[PATH_MAX+1];
|
||||
is_bare_repository_cfg = 1;
|
||||
setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 0);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
} else if (!strcmp(cmd, "-c")) {
|
||||
if (*argc < 2) {
|
||||
fprintf(stderr, "-c expects a configuration string\n" );
|
||||
usage(git_usage_string);
|
||||
}
|
||||
git_config_push_parameter((*argv)[1]);
|
||||
(*argv)++;
|
||||
(*argc)--;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown option: %s\n", cmd);
|
||||
usage(git_usage_string);
|
||||
}
|
||||
|
||||
(*argv)++;
|
||||
(*argc)--;
|
||||
}
|
||||
return (*argv) - orig_argv;
|
||||
}
|
||||
|
||||
static int handle_alias(int *argcp, const char ***argv)
|
||||
{
|
||||
int envchanged = 0, ret = 0, saved_errno = errno;
|
||||
const char *subdir;
|
||||
int count, option_count;
|
||||
const char **new_argv;
|
||||
const char *alias_command;
|
||||
char *alias_string;
|
||||
int unused_nongit;
|
||||
|
||||
subdir = setup_git_directory_gently(&unused_nongit);
|
||||
|
||||
alias_command = (*argv)[0];
|
||||
alias_string = alias_lookup(alias_command);
|
||||
if (alias_string) {
|
||||
if (alias_string[0] == '!') {
|
||||
const char **alias_argv;
|
||||
int argc = *argcp, i;
|
||||
|
||||
commit_pager_choice();
|
||||
|
||||
/* build alias_argv */
|
||||
alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));
|
||||
alias_argv[0] = alias_string + 1;
|
||||
for (i = 1; i < argc; ++i)
|
||||
alias_argv[i] = (*argv)[i];
|
||||
alias_argv[argc] = NULL;
|
||||
|
||||
ret = run_command_v_opt(alias_argv, RUN_USING_SHELL);
|
||||
if (ret >= 0) /* normal exit */
|
||||
exit(ret);
|
||||
|
||||
die_errno("While expanding alias '%s': '%s'",
|
||||
alias_command, alias_string + 1);
|
||||
}
|
||||
count = split_cmdline(alias_string, &new_argv);
|
||||
if (count < 0)
|
||||
die("Bad alias.%s string: %s", alias_command,
|
||||
split_cmdline_strerror(count));
|
||||
option_count = handle_options(&new_argv, &count, &envchanged);
|
||||
if (envchanged)
|
||||
die("alias '%s' changes environment variables\n"
|
||||
"You can use '!git' in the alias to do this.",
|
||||
alias_command);
|
||||
memmove(new_argv - option_count, new_argv,
|
||||
count * sizeof(char *));
|
||||
new_argv -= option_count;
|
||||
|
||||
if (count < 1)
|
||||
die("empty alias for %s", alias_command);
|
||||
|
||||
if (!strcmp(alias_command, new_argv[0]))
|
||||
die("recursive alias: %s", alias_command);
|
||||
|
||||
trace_argv_printf(new_argv,
|
||||
"trace: alias expansion: %s =>",
|
||||
alias_command);
|
||||
|
||||
new_argv = xrealloc(new_argv, sizeof(char *) *
|
||||
(count + *argcp));
|
||||
/* insert after command name */
|
||||
memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
|
||||
|
||||
*argv = new_argv;
|
||||
*argcp += count - 1;
|
||||
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
if (subdir && chdir(subdir))
|
||||
die_errno("Cannot change to '%s'", subdir);
|
||||
|
||||
errno = saved_errno;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char git_version_string[] = GIT_VERSION;
|
||||
|
||||
#define RUN_SETUP (1<<0)
|
||||
#define RUN_SETUP_GENTLY (1<<1)
|
||||
#define USE_PAGER (1<<2)
|
||||
/*
|
||||
* require working tree to be present -- anything uses this needs
|
||||
* RUN_SETUP for reading from the configuration file.
|
||||
*/
|
||||
#define NEED_WORK_TREE (1<<3)
|
||||
|
||||
struct cmd_struct {
|
||||
const char *cmd;
|
||||
int (*fn)(int, const char **, const char *);
|
||||
int option;
|
||||
};
|
||||
|
||||
static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
|
||||
{
|
||||
int status, help;
|
||||
struct stat st;
|
||||
const char *prefix;
|
||||
|
||||
prefix = NULL;
|
||||
help = argc == 2 && !strcmp(argv[1], "-h");
|
||||
if (!help) {
|
||||
if (p->option & RUN_SETUP)
|
||||
prefix = setup_git_directory();
|
||||
if (p->option & RUN_SETUP_GENTLY) {
|
||||
int nongit_ok;
|
||||
prefix = setup_git_directory_gently(&nongit_ok);
|
||||
}
|
||||
|
||||
if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY))
|
||||
use_pager = check_pager_config(p->cmd);
|
||||
if (use_pager == -1 && p->option & USE_PAGER)
|
||||
use_pager = 1;
|
||||
|
||||
if ((p->option & (RUN_SETUP | RUN_SETUP_GENTLY)) &&
|
||||
startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */
|
||||
trace_repo_setup(prefix);
|
||||
}
|
||||
commit_pager_choice();
|
||||
|
||||
if (!help && p->option & NEED_WORK_TREE)
|
||||
setup_work_tree();
|
||||
|
||||
trace_argv_printf(argv, "trace: built-in: git");
|
||||
|
||||
status = p->fn(argc, argv, prefix);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/* Somebody closed stdout? */
|
||||
if (fstat(fileno(stdout), &st))
|
||||
return 0;
|
||||
/* Ignore write errors for pipes and sockets.. */
|
||||
if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
|
||||
return 0;
|
||||
|
||||
/* Check for ENOSPC and EIO errors.. */
|
||||
if (fflush(stdout))
|
||||
die_errno("write failure on standard output");
|
||||
if (ferror(stdout))
|
||||
die("unknown write failure on standard output");
|
||||
if (fclose(stdout))
|
||||
die_errno("close failed on standard output");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handle_internal_command(int argc, const char **argv)
|
||||
{
|
||||
const char *cmd = argv[0];
|
||||
static struct cmd_struct commands[] = {
|
||||
{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "annotate", cmd_annotate, RUN_SETUP },
|
||||
{ "apply", cmd_apply, RUN_SETUP_GENTLY },
|
||||
{ "archive", cmd_archive },
|
||||
{ "bisect--helper", cmd_bisect__helper, RUN_SETUP },
|
||||
{ "blame", cmd_blame, RUN_SETUP },
|
||||
{ "branch", cmd_branch, RUN_SETUP },
|
||||
{ "bundle", cmd_bundle, RUN_SETUP_GENTLY },
|
||||
{ "cat-file", cmd_cat_file, RUN_SETUP },
|
||||
{ "check-attr", cmd_check_attr, RUN_SETUP },
|
||||
{ "check-ref-format", cmd_check_ref_format },
|
||||
{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "checkout-index", cmd_checkout_index,
|
||||
RUN_SETUP | NEED_WORK_TREE},
|
||||
{ "cherry", cmd_cherry, RUN_SETUP },
|
||||
{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "clone", cmd_clone },
|
||||
{ "column", cmd_column, RUN_SETUP_GENTLY },
|
||||
{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "commit-tree", cmd_commit_tree, RUN_SETUP },
|
||||
{ "config", cmd_config, RUN_SETUP_GENTLY },
|
||||
{ "count-objects", cmd_count_objects, RUN_SETUP },
|
||||
{ "describe", cmd_describe, RUN_SETUP },
|
||||
{ "diff", cmd_diff },
|
||||
{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "diff-index", cmd_diff_index, RUN_SETUP },
|
||||
{ "diff-tree", cmd_diff_tree, RUN_SETUP },
|
||||
{ "fast-export", cmd_fast_export, RUN_SETUP },
|
||||
{ "fetch", cmd_fetch, RUN_SETUP },
|
||||
{ "fetch-pack", cmd_fetch_pack, RUN_SETUP },
|
||||
{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
|
||||
{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
|
||||
{ "format-patch", cmd_format_patch, RUN_SETUP },
|
||||
{ "fsck", cmd_fsck, RUN_SETUP },
|
||||
{ "fsck-objects", cmd_fsck, RUN_SETUP },
|
||||
{ "gc", cmd_gc, RUN_SETUP },
|
||||
{ "get-tar-commit-id", cmd_get_tar_commit_id },
|
||||
{ "grep", cmd_grep, RUN_SETUP_GENTLY },
|
||||
{ "hash-object", cmd_hash_object },
|
||||
{ "help", cmd_help },
|
||||
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
|
||||
{ "init", cmd_init_db },
|
||||
{ "init-db", cmd_init_db },
|
||||
{ "log", cmd_log, RUN_SETUP },
|
||||
{ "ls-files", cmd_ls_files, RUN_SETUP },
|
||||
{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
|
||||
{ "ls-tree", cmd_ls_tree, RUN_SETUP },
|
||||
{ "mailinfo", cmd_mailinfo },
|
||||
{ "mailsplit", cmd_mailsplit },
|
||||
{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "merge-base", cmd_merge_base, RUN_SETUP },
|
||||
{ "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
|
||||
{ "merge-index", cmd_merge_index, RUN_SETUP },
|
||||
{ "merge-ours", cmd_merge_ours, RUN_SETUP },
|
||||
{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "merge-tree", cmd_merge_tree, RUN_SETUP },
|
||||
{ "mktag", cmd_mktag, RUN_SETUP },
|
||||
{ "mktree", cmd_mktree, RUN_SETUP },
|
||||
{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "name-rev", cmd_name_rev, RUN_SETUP },
|
||||
{ "notes", cmd_notes, RUN_SETUP },
|
||||
{ "pack-objects", cmd_pack_objects, RUN_SETUP },
|
||||
{ "pack-redundant", cmd_pack_redundant, RUN_SETUP },
|
||||
{ "pack-refs", cmd_pack_refs, RUN_SETUP },
|
||||
{ "patch-id", cmd_patch_id },
|
||||
{ "peek-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
|
||||
{ "pickaxe", cmd_blame, RUN_SETUP },
|
||||
{ "prune", cmd_prune, RUN_SETUP },
|
||||
{ "prune-packed", cmd_prune_packed, RUN_SETUP },
|
||||
{ "push", cmd_push, RUN_SETUP },
|
||||
{ "read-tree", cmd_read_tree, RUN_SETUP },
|
||||
{ "receive-pack", cmd_receive_pack },
|
||||
{ "reflog", cmd_reflog, RUN_SETUP },
|
||||
{ "remote", cmd_remote, RUN_SETUP },
|
||||
{ "remote-ext", cmd_remote_ext },
|
||||
{ "remote-fd", cmd_remote_fd },
|
||||
{ "replace", cmd_replace, RUN_SETUP },
|
||||
{ "repo-config", cmd_repo_config, RUN_SETUP_GENTLY },
|
||||
{ "rerere", cmd_rerere, RUN_SETUP },
|
||||
{ "reset", cmd_reset, RUN_SETUP },
|
||||
{ "rev-list", cmd_rev_list, RUN_SETUP },
|
||||
{ "rev-parse", cmd_rev_parse },
|
||||
{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "rm", cmd_rm, RUN_SETUP },
|
||||
{ "send-pack", cmd_send_pack, RUN_SETUP },
|
||||
{ "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
|
||||
{ "show", cmd_show, RUN_SETUP },
|
||||
{ "show-branch", cmd_show_branch, RUN_SETUP },
|
||||
{ "show-ref", cmd_show_ref, RUN_SETUP },
|
||||
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "stripspace", cmd_stripspace },
|
||||
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
|
||||
{ "tag", cmd_tag, RUN_SETUP },
|
||||
{ "tar-tree", cmd_tar_tree },
|
||||
{ "unpack-file", cmd_unpack_file, RUN_SETUP },
|
||||
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
|
||||
{ "update-index", cmd_update_index, RUN_SETUP },
|
||||
{ "update-ref", cmd_update_ref, RUN_SETUP },
|
||||
{ "update-server-info", cmd_update_server_info, RUN_SETUP },
|
||||
{ "upload-archive", cmd_upload_archive },
|
||||
{ "upload-archive--writer", cmd_upload_archive_writer },
|
||||
{ "var", cmd_var, RUN_SETUP_GENTLY },
|
||||
{ "verify-pack", cmd_verify_pack },
|
||||
{ "verify-tag", cmd_verify_tag, RUN_SETUP },
|
||||
{ "version", cmd_version },
|
||||
{ "whatchanged", cmd_whatchanged, RUN_SETUP },
|
||||
{ "write-tree", cmd_write_tree, RUN_SETUP },
|
||||
};
|
||||
int i;
|
||||
static const char ext[] = STRIP_EXTENSION;
|
||||
|
||||
if (sizeof(ext) > 1) {
|
||||
i = strlen(argv[0]) - strlen(ext);
|
||||
if (i > 0 && !strcmp(argv[0] + i, ext)) {
|
||||
char *argv0 = xstrdup(argv[0]);
|
||||
argv[0] = cmd = argv0;
|
||||
argv0[i] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/* Turn "git cmd --help" into "git help cmd" */
|
||||
if (argc > 1 && !strcmp(argv[1], "--help")) {
|
||||
argv[1] = argv[0];
|
||||
argv[0] = cmd = "help";
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(commands); i++) {
|
||||
struct cmd_struct *p = commands+i;
|
||||
if (strcmp(p->cmd, cmd))
|
||||
continue;
|
||||
exit(run_builtin(p, argc, argv));
|
||||
}
|
||||
}
|
||||
|
||||
static void execv_dashed_external(const char **argv)
|
||||
{
|
||||
struct strbuf cmd = STRBUF_INIT;
|
||||
const char *tmp;
|
||||
int status;
|
||||
|
||||
if (use_pager == -1)
|
||||
use_pager = check_pager_config(argv[0]);
|
||||
commit_pager_choice();
|
||||
|
||||
strbuf_addf(&cmd, "git-%s", argv[0]);
|
||||
|
||||
/*
|
||||
* argv[0] must be the git command, but the argv array
|
||||
* belongs to the caller, and may be reused in
|
||||
* subsequent loop iterations. Save argv[0] and
|
||||
* restore it on error.
|
||||
*/
|
||||
tmp = argv[0];
|
||||
argv[0] = cmd.buf;
|
||||
|
||||
trace_argv_printf(argv, "trace: exec:");
|
||||
|
||||
/*
|
||||
* if we fail because the command is not found, it is
|
||||
* OK to return. Otherwise, we just pass along the status code.
|
||||
*/
|
||||
status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE | RUN_CLEAN_ON_EXIT);
|
||||
if (status >= 0 || errno != ENOENT)
|
||||
exit(status);
|
||||
|
||||
argv[0] = tmp;
|
||||
|
||||
strbuf_release(&cmd);
|
||||
}
|
||||
|
||||
static int run_argv(int *argcp, const char ***argv)
|
||||
{
|
||||
int done_alias = 0;
|
||||
|
||||
while (1) {
|
||||
/* See if it's an internal command */
|
||||
handle_internal_command(*argcp, *argv);
|
||||
|
||||
/* .. then try the external ones */
|
||||
execv_dashed_external(*argv);
|
||||
|
||||
/* It could be an alias -- this works around the insanity
|
||||
* of overriding "git log" with "git show" by having
|
||||
* alias.log = show
|
||||
*/
|
||||
if (done_alias || !handle_alias(argcp, argv))
|
||||
break;
|
||||
done_alias = 1;
|
||||
}
|
||||
|
||||
return done_alias;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
const char *cmd;
|
||||
|
||||
startup_info = &git_startup_info;
|
||||
|
||||
cmd = git_extract_argv0_path(argv[0]);
|
||||
if (!cmd)
|
||||
cmd = "git-help";
|
||||
|
||||
git_setup_gettext();
|
||||
|
||||
/*
|
||||
* "git-xxxx" is the same as "git xxxx", but we obviously:
|
||||
*
|
||||
* - cannot take flags in between the "git" and the "xxxx".
|
||||
* - cannot execute it externally (since it would just do
|
||||
* the same thing over again)
|
||||
*
|
||||
* So we just directly call the internal command handler, and
|
||||
* die if that one cannot handle it.
|
||||
*/
|
||||
if (!prefixcmp(cmd, "git-")) {
|
||||
cmd += 4;
|
||||
argv[0] = cmd;
|
||||
handle_internal_command(argc, argv);
|
||||
die("cannot handle %s internally", cmd);
|
||||
}
|
||||
|
||||
/* Look for flags.. */
|
||||
argv++;
|
||||
argc--;
|
||||
handle_options(&argv, &argc, NULL);
|
||||
if (argc > 0) {
|
||||
if (!prefixcmp(argv[0], "--"))
|
||||
argv[0] += 2;
|
||||
} else {
|
||||
/* The user didn't specify a command; give them help */
|
||||
commit_pager_choice();
|
||||
printf("usage: %s\n\n", git_usage_string);
|
||||
list_common_cmds_help();
|
||||
printf("\n%s\n", git_more_info_string);
|
||||
exit(1);
|
||||
}
|
||||
cmd = argv[0];
|
||||
|
||||
/*
|
||||
* We use PATH to find git commands, but we prepend some higher
|
||||
* precedence paths: the "--exec-path" option, the GIT_EXEC_PATH
|
||||
* environment, and the $(gitexecdir) from the Makefile at build
|
||||
* time.
|
||||
*/
|
||||
setup_path();
|
||||
|
||||
while (1) {
|
||||
static int done_help = 0;
|
||||
static int was_alias = 0;
|
||||
was_alias = run_argv(&argc, &argv);
|
||||
if (errno != ENOENT)
|
||||
break;
|
||||
if (was_alias) {
|
||||
fprintf(stderr, "Expansion of alias '%s' failed; "
|
||||
"'%s' is not a git command\n",
|
||||
cmd, argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
if (!done_help) {
|
||||
cmd = argv[0] = help_unknown_cmd(cmd);
|
||||
done_help = 1;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Failed to run command '%s': %s\n",
|
||||
cmd, strerror(errno));
|
||||
|
||||
return 1;
|
||||
}
|
||||
74
samples/C/hash.c
Normal file
74
samples/C/hash.c
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "hash.h"
|
||||
|
||||
#if defined(PPC_SHA1)
|
||||
# include "ppc/sha1.h"
|
||||
#else
|
||||
# include "sha1.h"
|
||||
#endif
|
||||
|
||||
struct git_hash_ctx {
|
||||
SHA_CTX c;
|
||||
};
|
||||
|
||||
git_hash_ctx *git_hash_new_ctx(void)
|
||||
{
|
||||
git_hash_ctx *ctx = git__malloc(sizeof(*ctx));
|
||||
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
SHA1_Init(&ctx->c);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void git_hash_free_ctx(git_hash_ctx *ctx)
|
||||
{
|
||||
git__free(ctx);
|
||||
}
|
||||
|
||||
void git_hash_init(git_hash_ctx *ctx)
|
||||
{
|
||||
assert(ctx);
|
||||
SHA1_Init(&ctx->c);
|
||||
}
|
||||
|
||||
void git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
|
||||
{
|
||||
assert(ctx);
|
||||
SHA1_Update(&ctx->c, data, len);
|
||||
}
|
||||
|
||||
void git_hash_final(git_oid *out, git_hash_ctx *ctx)
|
||||
{
|
||||
assert(ctx);
|
||||
SHA1_Final(out->id, &ctx->c);
|
||||
}
|
||||
|
||||
void git_hash_buf(git_oid *out, const void *data, size_t len)
|
||||
{
|
||||
SHA_CTX c;
|
||||
|
||||
SHA1_Init(&c);
|
||||
SHA1_Update(&c, data, len);
|
||||
SHA1_Final(out->id, &c);
|
||||
}
|
||||
|
||||
void git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n)
|
||||
{
|
||||
SHA_CTX c;
|
||||
size_t i;
|
||||
|
||||
SHA1_Init(&c);
|
||||
for (i = 0; i < n; i++)
|
||||
SHA1_Update(&c, vec[i].data, vec[i].len);
|
||||
SHA1_Final(out->id, &c);
|
||||
}
|
||||
7
samples/C/hello.c
Normal file
7
samples/C/hello.c
Normal file
@@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Hello World\n");
|
||||
return 0;
|
||||
}
|
||||
6
samples/C/hello.h
Normal file
6
samples/C/hello.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef HELLO_H
|
||||
#define HELLO_H
|
||||
|
||||
void hello();
|
||||
|
||||
#endif
|
||||
2059
samples/C/http_parser.c
Normal file
2059
samples/C/http_parser.c
Normal file
File diff suppressed because it is too large
Load Diff
318
samples/C/http_parser.h
Normal file
318
samples/C/http_parser.h
Normal file
@@ -0,0 +1,318 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef http_parser_h
|
||||
#define http_parser_h
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define HTTP_PARSER_VERSION_MAJOR 1
|
||||
#define HTTP_PARSER_VERSION_MINOR 0
|
||||
|
||||
#include <sys/types.h>
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
|
||||
typedef __int8 int8_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
typedef unsigned int size_t;
|
||||
typedef int ssize_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
|
||||
* faster
|
||||
*/
|
||||
#ifndef HTTP_PARSER_STRICT
|
||||
# define HTTP_PARSER_STRICT 1
|
||||
#endif
|
||||
|
||||
/* Compile with -DHTTP_PARSER_DEBUG=1 to add extra debugging information to
|
||||
* the error reporting facility.
|
||||
*/
|
||||
#ifndef HTTP_PARSER_DEBUG
|
||||
# define HTTP_PARSER_DEBUG 0
|
||||
#endif
|
||||
|
||||
|
||||
/* Maximium header size allowed */
|
||||
#define HTTP_MAX_HEADER_SIZE (80*1024)
|
||||
|
||||
|
||||
typedef struct http_parser http_parser;
|
||||
typedef struct http_parser_settings http_parser_settings;
|
||||
|
||||
|
||||
/* Callbacks should return non-zero to indicate an error. The parser will
|
||||
* then halt execution.
|
||||
*
|
||||
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
|
||||
* returning '1' from on_headers_complete will tell the parser that it
|
||||
* should not expect a body. This is used when receiving a response to a
|
||||
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
|
||||
* chunked' headers that indicate the presence of a body.
|
||||
*
|
||||
* http_data_cb does not return data chunks. It will be call arbitrarally
|
||||
* many times for each string. E.G. you might get 10 callbacks for "on_path"
|
||||
* each providing just a few characters more data.
|
||||
*/
|
||||
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
||||
typedef int (*http_cb) (http_parser*);
|
||||
|
||||
|
||||
/* Request Methods */
|
||||
#define HTTP_METHOD_MAP(XX) \
|
||||
XX(0, DELETE, DELETE) \
|
||||
XX(1, GET, GET) \
|
||||
XX(2, HEAD, HEAD) \
|
||||
XX(3, POST, POST) \
|
||||
XX(4, PUT, PUT) \
|
||||
/* pathological */ \
|
||||
XX(5, CONNECT, CONNECT) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(7, TRACE, TRACE) \
|
||||
/* webdav */ \
|
||||
XX(8, COPY, COPY) \
|
||||
XX(9, LOCK, LOCK) \
|
||||
XX(10, MKCOL, MKCOL) \
|
||||
XX(11, MOVE, MOVE) \
|
||||
XX(12, PROPFIND, PROPFIND) \
|
||||
XX(13, PROPPATCH, PROPPATCH) \
|
||||
XX(14, SEARCH, SEARCH) \
|
||||
XX(15, UNLOCK, UNLOCK) \
|
||||
/* subversion */ \
|
||||
XX(16, REPORT, REPORT) \
|
||||
XX(17, MKACTIVITY, MKACTIVITY) \
|
||||
XX(18, CHECKOUT, CHECKOUT) \
|
||||
XX(19, MERGE, MERGE) \
|
||||
/* upnp */ \
|
||||
XX(20, MSEARCH, M-SEARCH) \
|
||||
XX(21, NOTIFY, NOTIFY) \
|
||||
XX(22, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
/* RFC-5789 */ \
|
||||
XX(24, PATCH, PATCH) \
|
||||
XX(25, PURGE, PURGE) \
|
||||
|
||||
enum http_method
|
||||
{
|
||||
#define XX(num, name, string) HTTP_##name = num,
|
||||
HTTP_METHOD_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
|
||||
|
||||
|
||||
/* Flag values for http_parser.flags field */
|
||||
enum flags
|
||||
{ F_CHUNKED = 1 << 0
|
||||
, F_CONNECTION_KEEP_ALIVE = 1 << 1
|
||||
, F_CONNECTION_CLOSE = 1 << 2
|
||||
, F_TRAILING = 1 << 3
|
||||
, F_UPGRADE = 1 << 4
|
||||
, F_SKIPBODY = 1 << 5
|
||||
};
|
||||
|
||||
|
||||
/* Map for errno-related constants
|
||||
*
|
||||
* The provided argument should be a macro that takes 2 arguments.
|
||||
*/
|
||||
#define HTTP_ERRNO_MAP(XX) \
|
||||
/* No error */ \
|
||||
XX(OK, "success") \
|
||||
\
|
||||
/* Callback-related errors */ \
|
||||
XX(CB_message_begin, "the on_message_begin callback failed") \
|
||||
XX(CB_url, "the on_url callback failed") \
|
||||
XX(CB_header_field, "the on_header_field callback failed") \
|
||||
XX(CB_header_value, "the on_header_value callback failed") \
|
||||
XX(CB_headers_complete, "the on_headers_complete callback failed") \
|
||||
XX(CB_body, "the on_body callback failed") \
|
||||
XX(CB_message_complete, "the on_message_complete callback failed") \
|
||||
\
|
||||
/* Parsing-related errors */ \
|
||||
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
|
||||
XX(HEADER_OVERFLOW, \
|
||||
"too many header bytes seen; overflow detected") \
|
||||
XX(CLOSED_CONNECTION, \
|
||||
"data received after completed connection: close message") \
|
||||
XX(INVALID_VERSION, "invalid HTTP version") \
|
||||
XX(INVALID_STATUS, "invalid HTTP status code") \
|
||||
XX(INVALID_METHOD, "invalid HTTP method") \
|
||||
XX(INVALID_URL, "invalid URL") \
|
||||
XX(INVALID_HOST, "invalid host") \
|
||||
XX(INVALID_PORT, "invalid port") \
|
||||
XX(INVALID_PATH, "invalid path") \
|
||||
XX(INVALID_QUERY_STRING, "invalid query string") \
|
||||
XX(INVALID_FRAGMENT, "invalid fragment") \
|
||||
XX(LF_EXPECTED, "LF character expected") \
|
||||
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
|
||||
XX(INVALID_CONTENT_LENGTH, \
|
||||
"invalid character in content-length header") \
|
||||
XX(INVALID_CHUNK_SIZE, \
|
||||
"invalid character in chunk size header") \
|
||||
XX(INVALID_CONSTANT, "invalid constant string") \
|
||||
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
|
||||
XX(STRICT, "strict mode assertion failed") \
|
||||
XX(PAUSED, "parser is paused") \
|
||||
XX(UNKNOWN, "an unknown error occurred")
|
||||
|
||||
|
||||
/* Define HPE_* values for each errno value above */
|
||||
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
|
||||
enum http_errno {
|
||||
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
|
||||
};
|
||||
#undef HTTP_ERRNO_GEN
|
||||
|
||||
|
||||
/* Get an http_errno value from an http_parser */
|
||||
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
|
||||
|
||||
/* Get the line number that generated the current error */
|
||||
#if HTTP_PARSER_DEBUG
|
||||
#define HTTP_PARSER_ERRNO_LINE(p) ((p)->error_lineno)
|
||||
#else
|
||||
#define HTTP_PARSER_ERRNO_LINE(p) 0
|
||||
#endif
|
||||
|
||||
|
||||
struct http_parser {
|
||||
/** PRIVATE **/
|
||||
unsigned char type : 2; /* enum http_parser_type */
|
||||
unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */
|
||||
unsigned char state; /* enum state from http_parser.c */
|
||||
unsigned char header_state; /* enum header_state from http_parser.c */
|
||||
unsigned char index; /* index into current matcher */
|
||||
|
||||
uint32_t nread; /* # bytes read in various scenarios */
|
||||
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
|
||||
|
||||
/** READ-ONLY **/
|
||||
unsigned short http_major;
|
||||
unsigned short http_minor;
|
||||
unsigned short status_code; /* responses only */
|
||||
unsigned char method; /* requests only */
|
||||
unsigned char http_errno : 7;
|
||||
|
||||
/* 1 = Upgrade header was present and the parser has exited because of that.
|
||||
* 0 = No upgrade header present.
|
||||
* Should be checked when http_parser_execute() returns in addition to
|
||||
* error checking.
|
||||
*/
|
||||
unsigned char upgrade : 1;
|
||||
|
||||
#if HTTP_PARSER_DEBUG
|
||||
uint32_t error_lineno;
|
||||
#endif
|
||||
|
||||
/** PUBLIC **/
|
||||
void *data; /* A pointer to get hook to the "connection" or "socket" object */
|
||||
};
|
||||
|
||||
|
||||
struct http_parser_settings {
|
||||
http_cb on_message_begin;
|
||||
http_data_cb on_url;
|
||||
http_data_cb on_header_field;
|
||||
http_data_cb on_header_value;
|
||||
http_cb on_headers_complete;
|
||||
http_data_cb on_body;
|
||||
http_cb on_message_complete;
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_url_fields
|
||||
{ UF_SCHEMA = 0
|
||||
, UF_HOST = 1
|
||||
, UF_PORT = 2
|
||||
, UF_PATH = 3
|
||||
, UF_QUERY = 4
|
||||
, UF_FRAGMENT = 5
|
||||
, UF_MAX = 6
|
||||
};
|
||||
|
||||
|
||||
/* Result structure for http_parser_parse_url().
|
||||
*
|
||||
* Callers should index into field_data[] with UF_* values iff field_set
|
||||
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
|
||||
* because we probably have padding left over), we convert any port to
|
||||
* a uint16_t.
|
||||
*/
|
||||
struct http_parser_url {
|
||||
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
|
||||
uint16_t port; /* Converted UF_PORT string */
|
||||
|
||||
struct {
|
||||
uint16_t off; /* Offset into buffer in which field starts */
|
||||
uint16_t len; /* Length of run in buffer */
|
||||
} field_data[UF_MAX];
|
||||
};
|
||||
|
||||
|
||||
void http_parser_init(http_parser *parser, enum http_parser_type type);
|
||||
|
||||
|
||||
size_t http_parser_execute(http_parser *parser,
|
||||
const http_parser_settings *settings,
|
||||
const char *data,
|
||||
size_t len);
|
||||
|
||||
|
||||
/* If http_should_keep_alive() in the on_headers_complete or
|
||||
* on_message_complete callback returns true, then this will be should be
|
||||
* the last message on the connection.
|
||||
* If you are the server, respond with the "Connection: close" header.
|
||||
* If you are the client, close the connection.
|
||||
*/
|
||||
int http_should_keep_alive(http_parser *parser);
|
||||
|
||||
/* Returns a string version of the HTTP method. */
|
||||
const char *http_method_str(enum http_method m);
|
||||
|
||||
/* Return a string name of the given error */
|
||||
const char *http_errno_name(enum http_errno err);
|
||||
|
||||
/* Return a string description of the given error */
|
||||
const char *http_errno_description(enum http_errno err);
|
||||
|
||||
/* Parse a URL; return nonzero on failure */
|
||||
int http_parser_parse_url(const char *buf, size_t buflen,
|
||||
int is_connect,
|
||||
struct http_parser_url *u);
|
||||
|
||||
/* Pause or un-pause the parser; a nonzero value pauses */
|
||||
void http_parser_pause(http_parser *parser, int paused);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
2551
samples/C/markdown.c
Normal file
2551
samples/C/markdown.c
Normal file
File diff suppressed because it is too large
Load Diff
462
samples/C/process.c
Normal file
462
samples/C/process.c
Normal file
@@ -0,0 +1,462 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && !TARGET_OS_IPHONE
|
||||
# include <crt_externs.h>
|
||||
# define environ (*_NSGetEnviron())
|
||||
#else
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
|
||||
static void uv__chld(EV_P_ ev_child* watcher, int revents) {
|
||||
int status = watcher->rstatus;
|
||||
int exit_status = 0;
|
||||
int term_signal = 0;
|
||||
uv_process_t *process = watcher->data;
|
||||
|
||||
assert(&process->child_watcher == watcher);
|
||||
assert(revents & EV_CHILD);
|
||||
|
||||
ev_child_stop(EV_A_ &process->child_watcher);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
exit_status = WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
if (WIFSIGNALED(status)) {
|
||||
term_signal = WTERMSIG(status);
|
||||
}
|
||||
|
||||
if (process->exit_cb) {
|
||||
process->exit_cb(process, exit_status, term_signal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv__make_socketpair(int fds[2], int flags) {
|
||||
#ifdef SOCK_NONBLOCK
|
||||
int fl;
|
||||
|
||||
fl = SOCK_CLOEXEC;
|
||||
|
||||
if (flags & UV__F_NONBLOCK)
|
||||
fl |= SOCK_NONBLOCK;
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM|fl, 0, fds) == 0)
|
||||
return 0;
|
||||
|
||||
if (errno != EINVAL)
|
||||
return -1;
|
||||
|
||||
/* errno == EINVAL so maybe the kernel headers lied about
|
||||
* the availability of SOCK_NONBLOCK. This can happen if people
|
||||
* build libuv against newer kernel headers than the kernel
|
||||
* they actually run the software on.
|
||||
*/
|
||||
#endif
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds))
|
||||
return -1;
|
||||
|
||||
uv__cloexec(fds[0], 1);
|
||||
uv__cloexec(fds[1], 1);
|
||||
|
||||
if (flags & UV__F_NONBLOCK) {
|
||||
uv__nonblock(fds[0], 1);
|
||||
uv__nonblock(fds[1], 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__make_pipe(int fds[2], int flags) {
|
||||
#if __linux__
|
||||
int fl;
|
||||
|
||||
fl = UV__O_CLOEXEC;
|
||||
|
||||
if (flags & UV__F_NONBLOCK)
|
||||
fl |= UV__O_NONBLOCK;
|
||||
|
||||
if (uv__pipe2(fds, fl) == 0)
|
||||
return 0;
|
||||
|
||||
if (errno != ENOSYS)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
if (pipe(fds))
|
||||
return -1;
|
||||
|
||||
uv__cloexec(fds[0], 1);
|
||||
uv__cloexec(fds[1], 1);
|
||||
|
||||
if (flags & UV__F_NONBLOCK) {
|
||||
uv__nonblock(fds[0], 1);
|
||||
uv__nonblock(fds[1], 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Used for initializing stdio streams like options.stdin_stream. Returns
|
||||
* zero on success.
|
||||
*/
|
||||
static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2],
|
||||
int writable) {
|
||||
int fd = -1;
|
||||
switch (container->flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD |
|
||||
UV_INHERIT_STREAM)) {
|
||||
case UV_IGNORE:
|
||||
return 0;
|
||||
case UV_CREATE_PIPE:
|
||||
assert(container->data.stream != NULL);
|
||||
|
||||
if (container->data.stream->type != UV_NAMED_PIPE) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return uv__make_socketpair(fds, 0);
|
||||
case UV_INHERIT_FD:
|
||||
case UV_INHERIT_STREAM:
|
||||
if (container->flags & UV_INHERIT_FD) {
|
||||
fd = container->data.fd;
|
||||
} else {
|
||||
fd = container->data.stream->fd;
|
||||
}
|
||||
|
||||
if (fd == -1) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fds[writable ? 1 : 0] = fd;
|
||||
|
||||
return 0;
|
||||
default:
|
||||
assert(0 && "Unexpected flags");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int uv__process_stdio_flags(uv_stdio_container_t* container,
|
||||
int writable) {
|
||||
if (container->data.stream->type == UV_NAMED_PIPE &&
|
||||
((uv_pipe_t*)container->data.stream)->ipc) {
|
||||
return UV_STREAM_READABLE | UV_STREAM_WRITABLE;
|
||||
} else if (writable) {
|
||||
return UV_STREAM_WRITABLE;
|
||||
} else {
|
||||
return UV_STREAM_READABLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int uv__process_open_stream(uv_stdio_container_t* container, int fds[2],
|
||||
int writable) {
|
||||
int fd = fds[writable ? 1 : 0];
|
||||
int child_fd = fds[writable ? 0 : 1];
|
||||
int flags;
|
||||
|
||||
/* No need to create stream */
|
||||
if (!(container->flags & UV_CREATE_PIPE) || fd < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(child_fd >= 0);
|
||||
close(child_fd);
|
||||
|
||||
uv__nonblock(fd, 1);
|
||||
flags = uv__process_stdio_flags(container, writable);
|
||||
|
||||
return uv__stream_open((uv_stream_t*)container->data.stream, fd, flags);
|
||||
}
|
||||
|
||||
|
||||
static void uv__process_close_stream(uv_stdio_container_t* container) {
|
||||
if (!(container->flags & UV_CREATE_PIPE)) return;
|
||||
|
||||
uv__stream_close((uv_stream_t*)container->data.stream);
|
||||
}
|
||||
|
||||
|
||||
static void uv__process_child_init(uv_process_options_t options,
|
||||
int stdio_count,
|
||||
int* pipes) {
|
||||
int i;
|
||||
|
||||
if (options.flags & UV_PROCESS_DETACHED) {
|
||||
setsid();
|
||||
}
|
||||
|
||||
/* Dup fds */
|
||||
for (i = 0; i < stdio_count; i++) {
|
||||
/*
|
||||
* stdin has swapped ends of pipe
|
||||
* (it's the only one readable stream)
|
||||
*/
|
||||
int close_fd = i == 0 ? pipes[i * 2 + 1] : pipes[i * 2];
|
||||
int use_fd = i == 0 ? pipes[i * 2] : pipes[i * 2 + 1];
|
||||
|
||||
if (use_fd >= 0) {
|
||||
close(close_fd);
|
||||
} else if (i < 3) {
|
||||
/* `/dev/null` stdin, stdout, stderr even if they've flag UV_IGNORE */
|
||||
use_fd = open("/dev/null", i == 0 ? O_RDONLY : O_RDWR);
|
||||
|
||||
if (use_fd < 0) {
|
||||
perror("failed to open stdio");
|
||||
_exit(127);
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i != use_fd) {
|
||||
dup2(use_fd, i);
|
||||
close(use_fd);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.cwd && chdir(options.cwd)) {
|
||||
perror("chdir()");
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) {
|
||||
perror("setgid()");
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
if ((options.flags & UV_PROCESS_SETUID) && setuid(options.uid)) {
|
||||
perror("setuid()");
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
environ = options.env;
|
||||
|
||||
execvp(options.file, options.args);
|
||||
perror("execvp()");
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
|
||||
#ifndef SPAWN_WAIT_EXEC
|
||||
# define SPAWN_WAIT_EXEC 1
|
||||
#endif
|
||||
|
||||
int uv_spawn(uv_loop_t* loop, uv_process_t* process,
|
||||
uv_process_options_t options) {
|
||||
/*
|
||||
* Save environ in the case that we get it clobbered
|
||||
* by the child process.
|
||||
*/
|
||||
char** save_our_env = environ;
|
||||
|
||||
int stdio_count = options.stdio_count < 3 ? 3 : options.stdio_count;
|
||||
int* pipes = malloc(2 * stdio_count * sizeof(int));
|
||||
|
||||
#if SPAWN_WAIT_EXEC
|
||||
int signal_pipe[2] = { -1, -1 };
|
||||
struct pollfd pfd;
|
||||
#endif
|
||||
int status;
|
||||
pid_t pid;
|
||||
int i;
|
||||
|
||||
if (pipes == NULL) {
|
||||
errno = ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
assert(options.file != NULL);
|
||||
assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS |
|
||||
UV_PROCESS_DETACHED |
|
||||
UV_PROCESS_SETGID |
|
||||
UV_PROCESS_SETUID)));
|
||||
|
||||
|
||||
uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
|
||||
loop->counters.process_init++;
|
||||
uv__handle_start(process);
|
||||
|
||||
process->exit_cb = options.exit_cb;
|
||||
|
||||
/* Init pipe pairs */
|
||||
for (i = 0; i < stdio_count; i++) {
|
||||
pipes[i * 2] = -1;
|
||||
pipes[i * 2 + 1] = -1;
|
||||
}
|
||||
|
||||
/* Create socketpairs/pipes, or use raw fd */
|
||||
for (i = 0; i < options.stdio_count; i++) {
|
||||
if (uv__process_init_stdio(&options.stdio[i], pipes + i * 2, i != 0)) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* This pipe is used by the parent to wait until
|
||||
* the child has called `execve()`. We need this
|
||||
* to avoid the following race condition:
|
||||
*
|
||||
* if ((pid = fork()) > 0) {
|
||||
* kill(pid, SIGTERM);
|
||||
* }
|
||||
* else if (pid == 0) {
|
||||
* execve("/bin/cat", argp, envp);
|
||||
* }
|
||||
*
|
||||
* The parent sends a signal immediately after forking.
|
||||
* Since the child may not have called `execve()` yet,
|
||||
* there is no telling what process receives the signal,
|
||||
* our fork or /bin/cat.
|
||||
*
|
||||
* To avoid ambiguity, we create a pipe with both ends
|
||||
* marked close-on-exec. Then, after the call to `fork()`,
|
||||
* the parent polls the read end until it sees POLLHUP.
|
||||
*/
|
||||
#if SPAWN_WAIT_EXEC
|
||||
if (uv__make_pipe(signal_pipe, UV__F_NONBLOCK))
|
||||
goto error;
|
||||
#endif
|
||||
|
||||
pid = fork();
|
||||
|
||||
if (pid == -1) {
|
||||
#if SPAWN_WAIT_EXEC
|
||||
close(signal_pipe[0]);
|
||||
close(signal_pipe[1]);
|
||||
#endif
|
||||
environ = save_our_env;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
/* Child */
|
||||
uv__process_child_init(options, stdio_count, pipes);
|
||||
|
||||
/* Execution never reaches here. */
|
||||
}
|
||||
|
||||
/* Parent. */
|
||||
|
||||
/* Restore environment. */
|
||||
environ = save_our_env;
|
||||
|
||||
#if SPAWN_WAIT_EXEC
|
||||
/* POLLHUP signals child has exited or execve()'d. */
|
||||
close(signal_pipe[1]);
|
||||
do {
|
||||
pfd.fd = signal_pipe[0];
|
||||
pfd.events = POLLIN|POLLHUP;
|
||||
pfd.revents = 0;
|
||||
errno = 0, status = poll(&pfd, 1, -1);
|
||||
}
|
||||
while (status == -1 && (errno == EINTR || errno == ENOMEM));
|
||||
|
||||
assert((status == 1) && "poll() on pipe read end failed");
|
||||
close(signal_pipe[0]);
|
||||
#endif
|
||||
|
||||
process->pid = pid;
|
||||
|
||||
ev_child_init(&process->child_watcher, uv__chld, pid, 0);
|
||||
ev_child_start(process->loop->ev, &process->child_watcher);
|
||||
process->child_watcher.data = process;
|
||||
|
||||
for (i = 0; i < options.stdio_count; i++) {
|
||||
if (uv__process_open_stream(&options.stdio[i], pipes + i * 2, i == 0)) {
|
||||
int j;
|
||||
/* Close all opened streams */
|
||||
for (j = 0; j < i; j++) {
|
||||
uv__process_close_stream(&options.stdio[j]);
|
||||
}
|
||||
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
free(pipes);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
uv__set_sys_error(process->loop, errno);
|
||||
|
||||
for (i = 0; i < stdio_count; i++) {
|
||||
close(pipes[i * 2]);
|
||||
close(pipes[i * 2 + 1]);
|
||||
}
|
||||
|
||||
free(pipes);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int uv_process_kill(uv_process_t* process, int signum) {
|
||||
int r = kill(process->pid, signum);
|
||||
|
||||
if (r) {
|
||||
uv__set_sys_error(process->loop, errno);
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uv_err_t uv_kill(int pid, int signum) {
|
||||
int r = kill(pid, signum);
|
||||
|
||||
if (r) {
|
||||
return uv__new_sys_error(errno);
|
||||
} else {
|
||||
return uv_ok_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uv__process_close(uv_process_t* handle) {
|
||||
ev_child_stop(handle->loop->ev, &handle->child_watcher);
|
||||
uv__handle_stop(handle);
|
||||
}
|
||||
129
samples/C/rdiscount.c
Normal file
129
samples/C/rdiscount.c
Normal file
@@ -0,0 +1,129 @@
|
||||
#include <stdio.h>
|
||||
#include "ruby.h"
|
||||
#include "mkdio.h"
|
||||
|
||||
static VALUE rb_cRDiscount;
|
||||
|
||||
static VALUE
|
||||
rb_rdiscount_to_html(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
/* grab char pointer to markdown input text */
|
||||
char *res;
|
||||
int szres;
|
||||
VALUE encoding;
|
||||
VALUE text = rb_funcall(self, rb_intern("text"), 0);
|
||||
VALUE buf = rb_str_buf_new(1024);
|
||||
Check_Type(text, T_STRING);
|
||||
|
||||
int flags = rb_rdiscount__get_flags(self);
|
||||
|
||||
MMIOT *doc = mkd_string(RSTRING_PTR(text), RSTRING_LEN(text), flags);
|
||||
|
||||
if ( mkd_compile(doc, flags) ) {
|
||||
szres = mkd_document(doc, &res);
|
||||
|
||||
if ( szres != EOF ) {
|
||||
rb_str_cat(buf, res, szres);
|
||||
rb_str_cat(buf, "\n", 1);
|
||||
}
|
||||
}
|
||||
mkd_cleanup(doc);
|
||||
|
||||
|
||||
/* force the input encoding */
|
||||
if ( rb_respond_to(text, rb_intern("encoding")) ) {
|
||||
encoding = rb_funcall(text, rb_intern("encoding"), 0);
|
||||
rb_funcall(buf, rb_intern("force_encoding"), 1, encoding);
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
rb_rdiscount_toc_content(int argc, VALUE *argv, VALUE self)
|
||||
{
|
||||
char *res;
|
||||
int szres;
|
||||
|
||||
int flags = rb_rdiscount__get_flags(self);
|
||||
|
||||
/* grab char pointer to markdown input text */
|
||||
VALUE text = rb_funcall(self, rb_intern("text"), 0);
|
||||
Check_Type(text, T_STRING);
|
||||
|
||||
/* allocate a ruby string buffer and wrap it in a stream */
|
||||
VALUE buf = rb_str_buf_new(4096);
|
||||
|
||||
MMIOT *doc = mkd_string(RSTRING_PTR(text), RSTRING_LEN(text), flags);
|
||||
|
||||
if ( mkd_compile(doc, flags) ) {
|
||||
szres = mkd_toc(doc, &res);
|
||||
|
||||
if ( szres != EOF ) {
|
||||
rb_str_cat(buf, res, szres);
|
||||
rb_str_cat(buf, "\n", 1);
|
||||
}
|
||||
}
|
||||
mkd_cleanup(doc);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
int rb_rdiscount__get_flags(VALUE ruby_obj)
|
||||
{
|
||||
/* compile flags */
|
||||
int flags = MKD_TABSTOP | MKD_NOHEADER;
|
||||
|
||||
/* smart */
|
||||
if ( rb_funcall(ruby_obj, rb_intern("smart"), 0) != Qtrue )
|
||||
flags = flags | MKD_NOPANTS;
|
||||
|
||||
/* filter_html */
|
||||
if ( rb_funcall(ruby_obj, rb_intern("filter_html"), 0) == Qtrue )
|
||||
flags = flags | MKD_NOHTML;
|
||||
|
||||
/* generate_toc */
|
||||
if ( rb_funcall(ruby_obj, rb_intern("generate_toc"), 0) == Qtrue)
|
||||
flags = flags | MKD_TOC;
|
||||
|
||||
/* no_image */
|
||||
if ( rb_funcall(ruby_obj, rb_intern("no_image"), 0) == Qtrue)
|
||||
flags = flags | MKD_NOIMAGE;
|
||||
|
||||
/* no_links */
|
||||
if ( rb_funcall(ruby_obj, rb_intern("no_links"), 0) == Qtrue)
|
||||
flags = flags | MKD_NOLINKS;
|
||||
|
||||
/* no_tables */
|
||||
if ( rb_funcall(ruby_obj, rb_intern("no_tables"), 0) == Qtrue)
|
||||
flags = flags | MKD_NOTABLES;
|
||||
|
||||
/* strict */
|
||||
if ( rb_funcall(ruby_obj, rb_intern("strict"), 0) == Qtrue)
|
||||
flags = flags | MKD_STRICT;
|
||||
|
||||
/* autolink */
|
||||
if ( rb_funcall(ruby_obj, rb_intern("autolink"), 0) == Qtrue)
|
||||
flags = flags | MKD_AUTOLINK;
|
||||
|
||||
/* safelink */
|
||||
if ( rb_funcall(ruby_obj, rb_intern("safelink"), 0) == Qtrue)
|
||||
flags = flags | MKD_SAFELINK;
|
||||
|
||||
/* no_pseudo_protocols */
|
||||
if ( rb_funcall(ruby_obj, rb_intern("no_pseudo_protocols"), 0) == Qtrue)
|
||||
flags = flags | MKD_NO_EXT;
|
||||
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
||||
void Init_rdiscount()
|
||||
{
|
||||
rb_cRDiscount = rb_define_class("RDiscount", rb_cObject);
|
||||
rb_define_method(rb_cRDiscount, "to_html", rb_rdiscount_to_html, -1);
|
||||
rb_define_method(rb_cRDiscount, "toc_content", rb_rdiscount_toc_content, -1);
|
||||
}
|
||||
|
||||
/* vim: set ts=4 sw=4: */
|
||||
2538
samples/C/redis.c
Normal file
2538
samples/C/redis.c
Normal file
File diff suppressed because it is too large
Load Diff
164
samples/C/yajl.c
Normal file
164
samples/C/yajl.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2010, Lloyd Hilaiel.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of Lloyd Hilaiel nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 "api/yajl_parse.h"
|
||||
#include "yajl_lex.h"
|
||||
#include "yajl_parser.h"
|
||||
#include "yajl_alloc.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
const char *
|
||||
yajl_status_to_string(yajl_status stat)
|
||||
{
|
||||
const char * statStr = "unknown";
|
||||
switch (stat) {
|
||||
case yajl_status_ok:
|
||||
statStr = "ok, no error";
|
||||
break;
|
||||
case yajl_status_client_canceled:
|
||||
statStr = "client canceled parse";
|
||||
break;
|
||||
case yajl_status_insufficient_data:
|
||||
statStr = "eof was met before the parse could complete";
|
||||
break;
|
||||
case yajl_status_error:
|
||||
statStr = "parse error";
|
||||
break;
|
||||
}
|
||||
return statStr;
|
||||
}
|
||||
|
||||
yajl_handle
|
||||
yajl_alloc(const yajl_callbacks * callbacks,
|
||||
const yajl_parser_config * config,
|
||||
const yajl_alloc_funcs * afs,
|
||||
void * ctx)
|
||||
{
|
||||
unsigned int allowComments = 0;
|
||||
unsigned int validateUTF8 = 0;
|
||||
yajl_handle hand = NULL;
|
||||
yajl_alloc_funcs afsBuffer;
|
||||
|
||||
/* first order of business is to set up memory allocation routines */
|
||||
if (afs != NULL) {
|
||||
if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
yajl_set_default_alloc_funcs(&afsBuffer);
|
||||
afs = &afsBuffer;
|
||||
}
|
||||
|
||||
hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t));
|
||||
|
||||
/* copy in pointers to allocation routines */
|
||||
memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
|
||||
|
||||
if (config != NULL) {
|
||||
allowComments = config->allowComments;
|
||||
validateUTF8 = config->checkUTF8;
|
||||
}
|
||||
|
||||
hand->callbacks = callbacks;
|
||||
hand->ctx = ctx;
|
||||
hand->lexer = yajl_lex_alloc(&(hand->alloc), allowComments, validateUTF8);
|
||||
hand->bytesConsumed = 0;
|
||||
hand->decodeBuf = yajl_buf_alloc(&(hand->alloc));
|
||||
yajl_bs_init(hand->stateStack, &(hand->alloc));
|
||||
|
||||
yajl_bs_push(hand->stateStack, yajl_state_start);
|
||||
|
||||
return hand;
|
||||
}
|
||||
|
||||
void
|
||||
yajl_reset_parser(yajl_handle hand) {
|
||||
hand->lexer = yajl_lex_realloc(hand->lexer);
|
||||
}
|
||||
|
||||
void
|
||||
yajl_free(yajl_handle handle)
|
||||
{
|
||||
yajl_bs_free(handle->stateStack);
|
||||
yajl_buf_free(handle->decodeBuf);
|
||||
yajl_lex_free(handle->lexer);
|
||||
YA_FREE(&(handle->alloc), handle);
|
||||
}
|
||||
|
||||
yajl_status
|
||||
yajl_parse(yajl_handle hand, const unsigned char * jsonText,
|
||||
unsigned int jsonTextLen)
|
||||
{
|
||||
yajl_status status;
|
||||
status = yajl_do_parse(hand, jsonText, jsonTextLen);
|
||||
return status;
|
||||
}
|
||||
|
||||
yajl_status
|
||||
yajl_parse_complete(yajl_handle hand)
|
||||
{
|
||||
/* The particular case we want to handle is a trailing number.
|
||||
* Further input consisting of digits could cause our interpretation
|
||||
* of the number to change (buffered "1" but "2" comes in).
|
||||
* A very simple approach to this is to inject whitespace to terminate
|
||||
* any number in the lex buffer.
|
||||
*/
|
||||
return yajl_parse(hand, (const unsigned char *)" ", 1);
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
yajl_get_error(yajl_handle hand, int verbose,
|
||||
const unsigned char * jsonText, unsigned int jsonTextLen)
|
||||
{
|
||||
return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
yajl_get_bytes_consumed(yajl_handle hand)
|
||||
{
|
||||
if (!hand) return 0;
|
||||
else return hand->bytesConsumed;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
yajl_free_error(yajl_handle hand, unsigned char * str)
|
||||
{
|
||||
/* use memory allocation functions if set */
|
||||
YA_FREE(&(hand->alloc), str);
|
||||
}
|
||||
|
||||
/* XXX: add utility routines to parse from file */
|
||||
Reference in New Issue
Block a user