| /* | 
 |  * GIT - The information manager from hell | 
 |  * | 
 |  * Copyright (C) Linus Torvalds, 2005 | 
 |  */ | 
 | #include "cache.h" | 
 | #include "exec_cmd.h" | 
 | #include "tag.h" | 
 | #include "tree.h" | 
 | #include "builtin.h" | 
 | #include "parse-options.h" | 
 | #include "diff.h" | 
 | #include "userdiff.h" | 
 |  | 
 | #define BATCH 1 | 
 | #define BATCH_CHECK 2 | 
 |  | 
 | static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size) | 
 | { | 
 | 	/* the parser in tag.c is useless here. */ | 
 | 	const char *endp = buf + size; | 
 | 	const char *cp = buf; | 
 |  | 
 | 	while (cp < endp) { | 
 | 		char c = *cp++; | 
 | 		if (c != '\n') | 
 | 			continue; | 
 | 		if (7 <= endp - cp && !memcmp("tagger ", cp, 7)) { | 
 | 			const char *tagger = cp; | 
 |  | 
 | 			/* Found the tagger line.  Copy out the contents | 
 | 			 * of the buffer so far. | 
 | 			 */ | 
 | 			write_or_die(1, buf, cp - buf); | 
 |  | 
 | 			/* | 
 | 			 * Do something intelligent, like pretty-printing | 
 | 			 * the date. | 
 | 			 */ | 
 | 			while (cp < endp) { | 
 | 				if (*cp++ == '\n') { | 
 | 					/* tagger to cp is a line | 
 | 					 * that has ident and time. | 
 | 					 */ | 
 | 					const char *sp = tagger; | 
 | 					char *ep; | 
 | 					unsigned long date; | 
 | 					long tz; | 
 | 					while (sp < cp && *sp != '>') | 
 | 						sp++; | 
 | 					if (sp == cp) { | 
 | 						/* give up */ | 
 | 						write_or_die(1, tagger, | 
 | 							     cp - tagger); | 
 | 						break; | 
 | 					} | 
 | 					while (sp < cp && | 
 | 					       !('0' <= *sp && *sp <= '9')) | 
 | 						sp++; | 
 | 					write_or_die(1, tagger, sp - tagger); | 
 | 					date = strtoul(sp, &ep, 10); | 
 | 					tz = strtol(ep, NULL, 10); | 
 | 					sp = show_date(date, tz, 0); | 
 | 					write_or_die(1, sp, strlen(sp)); | 
 | 					xwrite(1, "\n", 1); | 
 | 					break; | 
 | 				} | 
 | 			} | 
 | 			break; | 
 | 		} | 
 | 		if (cp < endp && *cp == '\n') | 
 | 			/* end of header */ | 
 | 			break; | 
 | 	} | 
 | 	/* At this point, we have copied out the header up to the end of | 
 | 	 * the tagger line and cp points at one past \n.  It could be the | 
 | 	 * next header line after the tagger line, or it could be another | 
 | 	 * \n that marks the end of the headers.  We need to copy out the | 
 | 	 * remainder as is. | 
 | 	 */ | 
 | 	if (cp < endp) | 
 | 		write_or_die(1, cp, endp - cp); | 
 | } | 
 |  | 
 | static int cat_one_file(int opt, const char *exp_type, const char *obj_name) | 
 | { | 
 | 	unsigned char sha1[20]; | 
 | 	enum object_type type; | 
 | 	char *buf; | 
 | 	unsigned long size; | 
 | 	struct object_context obj_context; | 
 |  | 
 | 	if (get_sha1_with_context(obj_name, sha1, &obj_context)) | 
 | 		die("Not a valid object name %s", obj_name); | 
 |  | 
 | 	buf = NULL; | 
 | 	switch (opt) { | 
 | 	case 't': | 
 | 		type = sha1_object_info(sha1, NULL); | 
 | 		if (type > 0) { | 
 | 			printf("%s\n", typename(type)); | 
 | 			return 0; | 
 | 		} | 
 | 		break; | 
 |  | 
 | 	case 's': | 
 | 		type = sha1_object_info(sha1, &size); | 
 | 		if (type > 0) { | 
 | 			printf("%lu\n", size); | 
 | 			return 0; | 
 | 		} | 
 | 		break; | 
 |  | 
 | 	case 'e': | 
 | 		return !has_sha1_file(sha1); | 
 |  | 
 | 	case 'p': | 
 | 		type = sha1_object_info(sha1, NULL); | 
 | 		if (type < 0) | 
 | 			die("Not a valid object name %s", obj_name); | 
 |  | 
 | 		/* custom pretty-print here */ | 
 | 		if (type == OBJ_TREE) { | 
 | 			const char *ls_args[3] = { NULL }; | 
 | 			ls_args[0] =  "ls-tree"; | 
 | 			ls_args[1] =  obj_name; | 
 | 			return cmd_ls_tree(2, ls_args, NULL); | 
 | 		} | 
 |  | 
 | 		buf = read_sha1_file(sha1, &type, &size); | 
 | 		if (!buf) | 
 | 			die("Cannot read object %s", obj_name); | 
 | 		if (type == OBJ_TAG) { | 
 | 			pprint_tag(sha1, buf, size); | 
 | 			return 0; | 
 | 		} | 
 |  | 
 | 		/* otherwise just spit out the data */ | 
 | 		break; | 
 |  | 
 | 	case 'c': | 
 | 		if (!obj_context.path[0]) | 
 | 			die("git cat-file --textconv %s: <object> must be <sha1:path>", | 
 | 			    obj_name); | 
 |  | 
 | 		if (!textconv_object(obj_context.path, obj_context.mode, sha1, &buf, &size)) | 
 | 			die("git cat-file --textconv: unable to run textconv on %s", | 
 | 			    obj_name); | 
 | 		break; | 
 |  | 
 | 	case 0: | 
 | 		buf = read_object_with_reference(sha1, exp_type, &size, NULL); | 
 | 		break; | 
 |  | 
 | 	default: | 
 | 		die("git cat-file: unknown option: %s", exp_type); | 
 | 	} | 
 |  | 
 | 	if (!buf) | 
 | 		die("git cat-file %s: bad file", obj_name); | 
 |  | 
 | 	write_or_die(1, buf, size); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int batch_one_object(const char *obj_name, int print_contents) | 
 | { | 
 | 	unsigned char sha1[20]; | 
 | 	enum object_type type = 0; | 
 | 	unsigned long size; | 
 | 	void *contents = contents; | 
 |  | 
 | 	if (!obj_name) | 
 | 	   return 1; | 
 |  | 
 | 	if (get_sha1(obj_name, sha1)) { | 
 | 		printf("%s missing\n", obj_name); | 
 | 		fflush(stdout); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	if (print_contents == BATCH) | 
 | 		contents = read_sha1_file(sha1, &type, &size); | 
 | 	else | 
 | 		type = sha1_object_info(sha1, &size); | 
 |  | 
 | 	if (type <= 0) { | 
 | 		printf("%s missing\n", obj_name); | 
 | 		fflush(stdout); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size); | 
 | 	fflush(stdout); | 
 |  | 
 | 	if (print_contents == BATCH) { | 
 | 		write_or_die(1, contents, size); | 
 | 		printf("\n"); | 
 | 		fflush(stdout); | 
 | 		free(contents); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int batch_objects(int print_contents) | 
 | { | 
 | 	struct strbuf buf = STRBUF_INIT; | 
 |  | 
 | 	while (strbuf_getline(&buf, stdin, '\n') != EOF) { | 
 | 		int error = batch_one_object(buf.buf, print_contents); | 
 | 		if (error) | 
 | 			return error; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const char * const cat_file_usage[] = { | 
 | 	"git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>", | 
 | 	"git cat-file (--batch|--batch-check) < <list_of_objects>", | 
 | 	NULL | 
 | }; | 
 |  | 
 | static int git_cat_file_config(const char *var, const char *value, void *cb) | 
 | { | 
 | 	switch (userdiff_config(var, value)) { | 
 | 	case 0: | 
 | 		break; | 
 | 	case -1: | 
 | 		return -1; | 
 | 	default: | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	return git_default_config(var, value, cb); | 
 | } | 
 |  | 
 | int cmd_cat_file(int argc, const char **argv, const char *prefix) | 
 | { | 
 | 	int opt = 0, batch = 0; | 
 | 	const char *exp_type = NULL, *obj_name = NULL; | 
 |  | 
 | 	const struct option options[] = { | 
 | 		OPT_GROUP("<type> can be one of: blob, tree, commit, tag"), | 
 | 		OPT_SET_INT('t', NULL, &opt, "show object type", 't'), | 
 | 		OPT_SET_INT('s', NULL, &opt, "show object size", 's'), | 
 | 		OPT_SET_INT('e', NULL, &opt, | 
 | 			    "exit with zero when there's no error", 'e'), | 
 | 		OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'), | 
 | 		OPT_SET_INT(0, "textconv", &opt, | 
 | 			    "for blob objects, run textconv on object's content", 'c'), | 
 | 		OPT_SET_INT(0, "batch", &batch, | 
 | 			    "show info and content of objects fed from the standard input", | 
 | 			    BATCH), | 
 | 		OPT_SET_INT(0, "batch-check", &batch, | 
 | 			    "show info about objects fed from the standard input", | 
 | 			    BATCH_CHECK), | 
 | 		OPT_END() | 
 | 	}; | 
 |  | 
 | 	git_config(git_cat_file_config, NULL); | 
 |  | 
 | 	if (argc != 3 && argc != 2) | 
 | 		usage_with_options(cat_file_usage, options); | 
 |  | 
 | 	argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0); | 
 |  | 
 | 	if (opt) { | 
 | 		if (argc == 1) | 
 | 			obj_name = argv[0]; | 
 | 		else | 
 | 			usage_with_options(cat_file_usage, options); | 
 | 	} | 
 | 	if (!opt && !batch) { | 
 | 		if (argc == 2) { | 
 | 			exp_type = argv[0]; | 
 | 			obj_name = argv[1]; | 
 | 		} else | 
 | 			usage_with_options(cat_file_usage, options); | 
 | 	} | 
 | 	if (batch && (opt || argc)) { | 
 | 		usage_with_options(cat_file_usage, options); | 
 | 	} | 
 |  | 
 | 	if (batch) | 
 | 		return batch_objects(batch); | 
 |  | 
 | 	return cat_one_file(opt, exp_type, obj_name); | 
 | } |