Kho tháng 6/2006

Thứ sáu, 30 Tháng sáu năm 2006 19:43:35 ICT

Nghe đài Last.FM gọn nhẹ với Shell.FM

Hôm nay là ngày âm nhạc quốc tế :D

Để nghe đài phát thanh last.fm có thể dùng Shell.FM - một tiện ích nhỏ và gọn, chỉ cần bàn phím và một dấu nhắc shell để sử dụng.

Nói tiện và gọn là vì ngay cả biên dịch cũng cực gọn. Bung nó ra, vào thư mục shell-fm. Chạy lệnh make. Xong. Bây giờ đã có shell-fm trong thư mục hiện thời. Muốn nghe gì thì cứ shell-fm lastfm://url. Xem trang web trên để biết thêm cách điều khiển.

Tiện và quá gọn nên chỉ hỗ trợ OSS. Do đó cũng chịu cùng khuyết điểm của nasd, mỗi lúc chỉ có một ứng dụng có thể điều khiển đầu ra âm thanh. Patch sau (dành cho bản SVN) dùng libao để hỗ trợ thêm các loại trình điều khiển âm thanh khác như.. nói chung, chủ yếu là ALSA. Gõ make shell-fm.ao để có shell-fm.ao - phiên bản hỗ trợ libao của shell-fm.

Index: play.c
===================================================================
--- play.c	(revision 38)
+++ play.c	(working copy)
@@ -23,11 +23,21 @@
 #include <signal.h>
 
 #include <sys/ioctl.h>
+#ifdef __HAVE_LIBAO__
+#include <ao/ao.h>
+#else
 #include <linux/soundcard.h>
+#endif
 
 struct stream {
 	FILE * streamfd;
+#ifdef __HAVE_LIBAO__
+	int driver_id;
+	ao_device *device;
+	ao_sample_format fmt;
+#else
 	int audiofd;
+#endif
 	pid_t parent;
 };
 
@@ -39,9 +49,49 @@
 
 void * findsync(register unsigned char *, unsigned);
 
+#ifdef __HAVE_LIBAO__
 void playback(FILE * streamfd) {
+	static int ao_initialized = 0;
 	struct stream data;
 	struct mad_decoder dec;
+
+	if (!ao_initialized) {
+		ao_initialize();
+		ao_initialized = 1;
+	}
+
+	memset(& data, 0, sizeof(struct stream));
+	
+	data.streamfd = streamfd;
+	data.driver_id = ao_default_driver_id();
+	data.parent = getppid();
+
+	if(-1 == data.driver_id) {
+		fprintf(stderr, "Unable to find any usable output device!\n");
+		return;
+	}
+
+	data.fmt.bits = 16;
+	data.fmt.rate = 44100;
+	data.fmt.channels = 2;
+	data.fmt.byte_format = AO_FMT_BIG;
+	data.device = ao_open_live(data.driver_id,&data.fmt,NULL);
+
+	if (NULL == data.device) {
+		fprintf(stderr, "Unable to open device. Errno: %d\n",errno);
+		return;
+	}
+
+	mad_decoder_init(& dec, & data, input, NULL, NULL, output, NULL, NULL);
+	mad_decoder_run(& dec, MAD_DECODER_MODE_SYNC);
+	mad_decoder_finish(& dec);
+
+	fprintf(stderr, "Reached end of stream.\n");
+}
+#else
+void playback(FILE * streamfd) {
+	struct stream data;
+	struct mad_decoder dec;
 	unsigned arg;
 
 	memset(& data, 0, sizeof(struct stream));
@@ -64,6 +114,7 @@
 
 	fprintf(stderr, "Reached end of stream.\n");
 }
+#endif
 
 static enum mad_flow input(void * data, struct mad_stream * stream) {
 	static unsigned nbyte = 0;
@@ -98,12 +149,60 @@
 	return MAD_FLOW_CONTINUE;
 }
 
+#ifdef __HAVE_LIBAO__
 static enum mad_flow output(
 		void * data,
 		const struct mad_header * head,
 		struct mad_pcm * pcm) {
 	struct stream * ptr = (struct stream *) data;
 
+	unsigned nchan = pcm->channels, rate = pcm->samplerate;
+	register unsigned nsample = pcm->length;
+	mad_fixed_t * left = pcm->samples[0], * right = pcm->samples[1];
+	char *stream, *stream_ptr;
+
+	head = NULL;
+
+	if (rate != ptr->fmt.rate || nchan != ptr->fmt.channels) {
+		ptr->fmt.rate = rate;
+		ptr->fmt.channels = nchan;
+		if (ptr->device != NULL)
+			ao_close(ptr->device);
+		ptr->device = ao_open_live(ptr->driver_id,&ptr->fmt,NULL);
+
+		if (NULL == ptr->device) {
+			fprintf(stderr, "Unable to open device. Errno: %d\n",errno);
+			return MAD_FLOW_BREAK;
+		}
+	}
+
+	stream_ptr = stream = malloc(pcm->length * (pcm->channels == 2 ? 4 : 2));
+	
+	while(nsample--) {
+		signed int sample;
+
+		sample = scale(* left++);
+		*stream_ptr++ = (sample & 0xFF);
+		*stream_ptr++ = (sample >> 8) & 0xFF;
+
+		if(nchan == 2) {
+			sample = scale(* right++);
+			*stream_ptr++ = (sample & 0xFF);
+			*stream_ptr++ = (sample >> 8) & 0xFF;
+		}
+	}
+	ao_play(ptr->device, stream, pcm->length * (pcm->channels == 2 ? 4 : 2));
+	free(stream);
+
+	return MAD_FLOW_CONTINUE;
+}
+#else
+static enum mad_flow output(
+		void * data,
+		const struct mad_header * head,
+		struct mad_pcm * pcm) {
+	struct stream * ptr = (struct stream *) data;
+
 	unsigned nchan = pcm->channels, rate = pcm->samplerate, arg;
 	register unsigned nsample = pcm->length;
 	mad_fixed_t * left = pcm->samples[0], * right = pcm->samples[1];
@@ -134,6 +233,7 @@
 
 	return MAD_FLOW_CONTINUE;
 }
+#endif
 
 signed scale(register mad_fixed_t sample) {
 	sample += (1L << (MAD_F_FRACBITS - 16));
Index: Makefile
===================================================================
--- Makefile	(revision 38)
+++ Makefile	(working copy)
@@ -33,6 +33,9 @@
 all	: $(CODE) $(HEAD)
 	$(CC) -o $(OUTPUT) $(CFLAGS) $(CODE) && /usr/bin/strip $(OUTPUT)
 
+$(OUTPUT).ao: $(CODE) $(HEAD)
+	$(CC) -o $(OUTPUT).ao -D__HAVE_LIBAO__ $(CFLAGS) $(CODE) `pkg-config ao --cflags --libs` && /usr/bin/strip $(OUTPUT).ao
+
 install	: $(all)
 	mkdir -p $(BINPATH)
 	install -m 755 shell-fm $(BINPATH)

Một ngày cuối tháng hết sức tốt đẹp. Không kể nguyên tháng sáu không tốt đẹp gì lắm. Và cả mấy tháng tới cũng chả tốt đẹp là bao khi phải tiếp tục ở tù.


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ sáu, 30 Tháng sáu năm 2006 19:31:39 ICT

Chạy nasd với ALSA

nasd (NAS server) chạy với Open Sound System. Nếu một chương trình khác đã chiếm quyền điều khiển /dev/dsp thì nasd sẽ không chạy. Tương tự, nếu nasd chiếm /dev/dsp thì những ứng dụng khác cũng câm. nasd mặc dù chưa hỗ trợ ALSA nhưng vẫn có thể tạm khắc phục bằng gói alsa-oss. Gói này có một chương trình tên là aoss dùng để bao các ứng dụng OSS lại, chạy với ALSA. Chạy nasd với aoss dễ òm:

aoss nasd

Bây giờ nasd có thể chung sống hoà bình với ít nhất là Rhythmbox.


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ sáu, 30 Tháng sáu năm 2006 19:23:38 ICT

Xong nassink

Đã cho patch lên bug 345633. Chạy tốt với Rhythmbox. Vẫn chưa xử lý cơ chế lỗi cho hợp lý nên có thể sẽ crash nếu vừa chạy vừa tắt nasd

Chỉ cần hai tập tin nassink.cnassink.h từ CVS, gắn patch trên bug 345633 vào rồi dùng lệnh sau để biên dịch:

gcc nassink.c `pkg-config --cflags gstreamer-0.10 gstreamer-plugins-base-0.10 --libs gstreamer-base-0.10` -DVERSION=\"0.10.3\" -DPACKAGE=\"nassink\" -DGST_PACKAGE_NAME=\"gst-plugins-bad\" -DGST_PACKAGE_ORIGIN=\"http://gstreamer.net\" -laudio -lgstaudio-0.10 -fPIC -DPIC -shared -o libgstnassink.so

Kết quả là libgstnassink.so. Cho nó vô /usr/lib/gstreamer-0.10. Dùng gst-inspect-0.10 sẽ thấy có một plugin mới là nassink.Kiểm tra nassink bằng lệnh sau:

gst-launch-0.10 audiotestsrc ! nassink

Hoặc kiểm tra màu mè hơn:

gst-launch-0.10 filesrc location=tập-tin-mp3 ! decodebin ! audioconvert !  audioresample ! nassink

Cần đặt biến AUDIOSERVER hoặc thêm tham số host=.... sau nassink trong lệnh trên

Để sử dụng nassink cho toàn hệ thống GNOME, đặt khoá GConf /system/gstreamer/0.10/default/audiosinknassink


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ năm, 29 Tháng sáu năm 2006 23:54:44 ICT

Tiếp nassink

nassink bị "not negotiated" là bởi vì gst_nassink_prepare luôn trả về FALSE. Lúc chỉnh mã, chẳng hiểu sao lại làm rơi rớt một dấu !. Hậu quả là hễ nó "prepare" thành công thì nó báo thất bại. :(

nassink đã biên dịch tốt, chạy được. Chạy

gst-launch-0.10 audiotestsrc ! audioconvert ! audioresample ! nassink

nó nghe "rè tè rè tè.." trong khi dùng alsasink nghe "teeeee". Đau.


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ năm, 29 Tháng sáu năm 2006 21:06:06 ICT

Bài hát Phần mềm Tự do (có phim minh hoạ)

.. dành cho những ai .. ba chấm .. tại đây: http://www.revolution-os.com/musicvideo.html

Nghe đồn RMS viết bài hát này. Nhưng may quá, ông không hát. Có một chút gì đó của châu Phi với nhạc đồng quê Mỹ trong đó.


Cập nhật 3 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | Nhạc

Thứ tư, 28 Tháng sáu năm 2006 00:09:54 ICT

nassink

Thử đóng vai người hùng, port nassink từ gstreamer 0.8 sang gstreamer 0.10. Hậu quả: mai phải coi thử coi bytes_per_sample của NAS là bao nhiêu, mò lại OSS coi segsize và segtotal là cái khỉ gì. Bởi vì nassink hết chạy. :(

Luộc xong gst_nassink_prepare sẽ phải ngó tiếp tới gst_nassink_delay, chả biết phải cài hàm này thế nào


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Chủ nhật, 25 Tháng sáu năm 2006 15:00:32 ICT

WinFS và GNOME Storage

Nhân bài báo WinFS is Dead, mò lại GNOME Storage. Vẫn không có gì thay đổi. Có vẻ như GNOME Storage chết còn sớm hơn WinFS.


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ bảy, 24 Tháng sáu năm 2006 23:04:32 ICT

Lỗi Rhythmbox plugin không hiện tiếng Việt

Tại vì các plugin import lại hàm gettext() (hàm _()) trong khi rb-python-module.c đã import hàm này dùm rồi, thông qua gettext.install(). Import _() đè lên cái cũ nên hậu quả là gettext hết chạy. Hi vọng đây là nguyên nhân gây ra lỗi 343081.


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ năm, 22 Tháng sáu năm 2006 14:10:54 ICT

Chọn rời rạc trong mấy cái danh sách của Gtk

bằng Ctrl-Shift-Click, Shift-Click không hoạt động.


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ tư, 21 Tháng sáu năm 2006 23:49:09 ICT

Nhạc nhọt qua ngày tháng

Tháng 12/2005

  • 27/11 - 4/12
    • Scorpions - 88
    • Blackmore's Night - 30
    • Alanis Morisette (quái đản, mình nghe Alanis từ tháng 12??)
  • 4/12 - 11/12
    • Blackmore's Night - 40
    • Queensrÿche - 28
    • Dream Theater - 21
    • ABBA - 19
    • To/Die/For - 16
  • 11/12 - 18/12
    • Nightwish - 42
    • Metallica - 30
    • Scorpions - 16
  • 25/12 - 1/1: Metallica - 9

Tháng 1/2006

  • 1/1 - 8/1: Lại Metallica - 15

  • 8/1 - 15/1

    • Queensrÿche - 12
    • Guns n' Roses - 7
      • Megadeth - 6
  • 15/1 - 22/1

    • Within Temptation - 28
    • Nightwish - 26
  • 23/1 - 30/1

    • Metallica - 21
    • Nightwish - 16
    • Dream Theater - 12
    • Queensrÿche - 11
  • 29/1 - 5/2: Scorpions, Dream Theater, Blackmore's Night (7-8)

  • 5/2 - 12/2

    • Rhapsody - 8
    • Metallica - 7
    • Apocalyptica - 6
  • 12/2 - 19/2

    • Apocalyptica - 50 (mèn)
    • Guns n' Roses - 11
    • Nightwish - 10
  • 19/2 - 26/2

    • Apocalyptica - 107 (má ơi)
    • Nightwish - 59
    • Metallica - 35
    • Queensrÿche - 33
    • Rhapsody - 32
    • Blackmore's Night - 24
    • Alanis Morisette - 20

Tháng 3/2006

  • 26/2 - 5/3
    • Apocalyptica - 50
    • Nightwish - 40
    • Rhapsody - 25
    • Metallica - 24
  • 5/3 - 12/3
    • Nightwish - 78
    • Within Temptation - 47
    • Nirvana - 40
  • 12/3 - 19/3
    • Metallica - 109
    • Apocalyptica - 66
    • Nightwish - 37
    • Ozzy Osbourne - 28
  • 19/3 - 26/3
    • Therion - 131
    • Apocalyptica - 50
    • Nightwish - 43

Tháng 4/2006

  • 26/3 - 2/4
    • Within Temptation - 189
    • The Cranberries - 98
  • 2/4 - 9/4
    • Therion - 67
    • Within Temptation - 61
    • Apocalyptica - 50
    • Metallica - 33
  • 9/4 - 16/4
    • Metallica - 49
    • Blackmore's Night - 19
  • 16/4 - 23/4
    • Metallica - 109
    • Moonspell - 50
    • Guns n' Roses - 45
  • 23/4 - 30/4
    • Moonspell - 27
    • Metallica - 20
    • Dream Theater - 19
    • Within Temptation - 19
    • Guns n' Roses - 17
    • Nightwish - 15
    • Rise Against - 12

Tháng 5/2006

  • 30/4 - 7/5
    • Apocalyptica - 35
    • Nightwish - 27
    • Metallica - 18
  • 7/5 - 14/5
    • Metallica - 39
    • Apocalyptica - 34
    • The Cranberries - 28
    • Blackmore's Night - 21
  • 14/5 - 21/5
    • Alanis Morisette - 52
    • Apocalyptica - 42
    • Metallica - 29
    • Leaves' Eyes - 26
    • Therion - 21
    • The Cranberries - 20
  • 21/5 - 28/5
    • The Cranberries - 58
    • Metallica - 36
    • Nightwish - 29
    • Therion - 29
    • Leaves' Eyes - 24

Đêm qua mơ dáng em đang ôm đàn tằng tăng tắng tăng...


Cập nhật 3 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | Nhạc

Thứ tư, 21 Tháng sáu năm 2006 22:59:13 ICT

Kiểm tra lỗi bộ nhớ bằng memtest86

Sử dụng memtest86 rất dễ. Cài memtest86 vào (ý là emerge memtest86), nó sẽ cài vào /boot/memtest86/memtest.bin. Thêm vào grub một dòng đại loại như:

title Memtest86
root (hd0,4)
kernel /memtest86/memtest.bin

Khởi động lại máy và chạy memtest từ grub. Kết quả thật mĩ mãn: 641 lỗi sau hai lần chạy (635 thuộc về test 8, 4 cho test 5 và 2 cho test 6).


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ hai, 19 Tháng sáu năm 2006 19:48:58 ICT

Tiếp tục git và perl

Phiên bản git.pl đã có thể push toàn bộ git.git lên một repository rỗng, git fsck-objects --full hoàn toàn không thành vấn đề.

Vấn đề bây giờ là .. có sự khác biệt về "pack". Có hai loại pack là pack bình thường và thin pack. Với pack bình thường thì base object của delta object phải nằm trong cùng pack. Trong khi với thin pack thì không bị ràng buộc điều kiện này. Do thin pack không cần kèm theo base object nên sẽ nhỏ gọn hơn và thích hợp truyền qua mạng hơn. Trong khi đó pack bình thường được dùng để lưu trong objects/pack - cũng có nghĩa là nếu lưu lại các pack được truyền qua mạng, cho vào objects/pack thì kho đó vi phạm nguyên tắc. http-fetch sử dụng nguyên tắc này. Do không có sự hỗ trợ từ phía kho http, http-fetch phải duyệt lần lượt từng object tính từ các đầu nhánh. Nếu móc một thin pack về, nó sẽ thiếu base object - vậy tìm base object ở đâu đây? Mặc dù git.pl/receive-pack.pl chạy, nhưng cũng không xài được vì http-fetch đình công :(

Nếu không giữ thin pack lại mà bung hết ra thì http-fetch cũng chạy. Nhưng http transport xem ra không hiệu quả chút nào nếu cứ phải chạy vòng vòng tìm từng object (không nén). Tuy nhiên, nếu không dùng http-fetch thì cũng có nghĩa là giờ phải làm luôn send-pack bằng Perl! git rev-list và thuật toán delta sẽ là hai cái nhức đầu nhất.

Dù gì thì gì, cũng coi như có được cái nền git khá ngon bằng perl rồi. Tuy chậm hơn nhiều so với bản bằng C nhưng lại chạy được ở những nơi không có gcc

#!/usr/bin/perl -w

use Compress::Zlib;
use Digest::SHA1;

$report_status = 0;
$capabilities = "report-status";
$capabilities_sent = 0;
$keep_packs = 1;
$write_pack = undef;
@cache = ();
$cache_size = 32;

sub cache_add {
	my ($buffer, $sha1, $type) = @_;
	while ($#cache > $cache_size) {
		shift @cache;
	}
	$cache[++$#cache] = { "sha1" => $sha1, "data" => $buffer, "type" => $type};
}

sub cache_search {
	my ($sha1, $type) = @_;
	foreach $item (@cache) {
		if ($item->{sha1} eq $sha1) {
			cache_add ($item->{data}, $item->{sha1}, $item->{type});
			$$type = $item->{type};
			return $item->{data};
		}
	}
	undef;
}

sub usage {
        die "git-receive-pack.pl <git-dir>";
}

sub validate_symref {
        my ($path) = @_;

        if (-l $path) {
                my $real_path = readlink $path;
                return $real_path =~ m,^refs/, ? 0 : -1;
        }
        open FH, $path or return -1;
        my $line = <FH>;
        close FH;
        $line =~ m,^ref: *refs/., ? 0 : -1;
}

sub check_repository_format {
        #TODO
        return 0;
}

sub setup_git_env {
        $git_dir = $ENV{"GIT_DIR"};
        $git_dir = '.git' if !defined($git_dir);

        $git_object_dir = $ENV{"GIT_OBJECT_DIRECTORY"};
        $git_object_dir = "$git_dir/objects"
                unless defined($git_object_dir);

        #$git_refs_dir = "$git_dir/refs";

        $git_index_file = $ENV{"GIT_INDEX_FILE"};
        $git_index_file = "$git_dir/index"
                unless defined($git_index_file);

        $git_graft_file = $ENV{"GIT_GRAFT_FILE"};
        $git_graft_file = "$git_dir/info/grafts"
                unless defined($git_graft_file);
}

sub resolve_ref {
        my ($path, $sha1, $reading) = @_;
        my $depth = 5; #MAXDEPTH
        my $len;

        while (1) {
                return 0 if --$depth < 0;

                if (! -f $path) {
                        return 0 if $reading || !defined($!{ENOENT});
                        $$sha1 = "0000000000000000000000000000000000000000";
                        return $path;
                }

                if (-l $path) {
                        my $real_path = readlink ($path);
                        if ($real_path =~ m,^refs/,) {
                                $path = "$git_dir/$real_path";
                                next;
                        }
                }

                open FH, $path or return 0;
                my $line = <FH>;
                close FH;

                chomp($line);
                if ($line =~ m/^ref: *(.*)$/) {
                        $path = "$git_dir/$1";
                        next;
                }

                $$sha1 = $line;
                return $path;
        }
        return $path;
}

sub read_ref {
        my ($filename, $sha1) = @_;
        resolve_ref($filename, $sha1, 1) ? 0 : -1;
}

sub packet_write {
        my ($fd, $text) = @_;

        my $len = length($text)+4;
        #printf STDERR "packet_write: %d %04x %s", $len, $len, $text;
        printf "%04x%s", $len, $text;
}

sub packet_flush {
        my ($fd) = @_;

        print "0000";
        #print STDERR "packet_flush\n";
}


@commands = ();

sub read_head_info {
        while (1) {
                my $len;
                my $ret = read(STDIN, $len, 4);
                die "packet_read error: $!\n" if !defined($ret) || $ret != 4;
                die "protocol error: bad line length character: <$len>" if $len !~ m/^[0-9a-fA-F]{4}$/;
                $len = hex($len);
                last if !$len;
                $len -= 4;
                my $line;
                $ret = read(STDIN, $line, $len);
                die "packet_read error: $!\n" if !defined($ret) || $ret != $len;
                die "protocol error: bad line length $len" if $len != length($line);
                #print STDERR "packet_read: $line\n";
                die "protocol error: expected old/new/ref, got $line" 
                        if $line !~ /^([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) ([^\0]+)(\0report-status)?$/;

                $report_status = 1 if defined($4);

                my $cmd = {
                        'old_sha1' => $1,
                        'new_sha1' => $2,
                        'ref_name' => $3,
                        'error_string' => 0,
                };
                $commands[$#commands+1] = $cmd;
        }
}

%PACK_TYPE = (
        OBJ_EXT => 0,
        OBJ_COMMIT => 1,
        OBJ_TREE => 2,
        OBJ_BLOB => 3,
        OBJ_TAG => 4,
        OBJ_DELTA => 7
);

%PACK_SIG = ( 1 => "commit", 2 => "tree", 3 => "blob", 4 => "tag" );

$pack_buffer = '';
$pack_count = 0;

sub fill {
        my ($min) = @_;

        if ($min > length($pack_buffer)) {
                my $buf;
                read(STDIN, $buf, 4096);
                $pack_buffer .= $buf;
        }
        substr($pack_buffer, 0, $min);
}

sub throw_away {
        my ($len) = @_;
        my $data = substr($pack_buffer, 0, $len);
        $pack_sha->add($data);
        print PACK $data if defined($write_pack);
        $pack_buffer = substr($pack_buffer, $len);
        $pack_count += $len;
}

sub fill_and_throw {
        my ($len) = @_;
        $a = fill($len);
        throw_away($len);
        $a;
}

%pack_blobs = ();

sub get_data {
        my ($size) = @_;

        my $x = inflateInit() or die "Cannot create a inflation stream\n";
        my $buffer = '';
        my ($output, $status, $c);
        while (1) {
                fill(1);
                $c = $pack_buffer;
                ($output, $status) = $x->inflate(\$c);
                throw_away(length($pack_buffer)-length($c));
                $buffer .= $output if $status == Z_OK or $status == Z_STREAM_END;
                last if $status != Z_OK;
        }
        die "inflation failed\n" unless $status == Z_STREAM_END && length($buffer) == $size;
        $buffer;
}

sub unpack_non_delta_entry {
        my ($type, $size) = @_;

        my $buffer = get_data($size);
        $buffer;
        #print STDERR "Got $type\n";
}

sub prepare_packed_git {
        %packed_sha1 = ();
        while (<$git_object_dir/pack/*.pack>) {
                my $pack_file = $_;
                my $idx_file = $pack_file;
                $idx_file =~ s/\.pack$/.idx/;
                next unless -e $idx_file;

                open IDX, "<$idx_file" or next;
                binmode IDX;
                my $buf;

                read IDX,$buf,4*256;
                my $count = unpack("N",substr($buf,4*255));
                my $blen = read(IDX,$buf,(4+20)*$count);
                my @idx = unpack("(NH40)$count",$buf);
                for ($i = 0;$i < $count;$i ++) {
                        $packed_sha1{$idx[$i*2+1]} = {"filename" => $pack_file, "pos" => $idx[$i*2]};
                }
        }
}

sub find_pack_entry {
        my ($sha1) = @_;
        defined($packed_sha1{$sha1});
}

sub read_packed_sha1 {
        my ($sha1, $type) = @_;

	my $buffer = cache_search($sha1, $type);
	return $buffer if defined($buffer);
	
        open FH, $packed_sha1{$sha1}->{filename}
                or die "Cannot open pack ".$packed_sha1{$sha1}->{filename}.".\n";
        binmode FH;
        my $c;
        seek FH, $packed_sha1{$sha1}->{pos}, 0;
        read FH, $c, 1;
        $c = ord($c);
        $$type = ($c >> 4) & 7;
        my $size = ($c & 15);
        my $shift = 4;
        while ($c & 0x80) {
                read FH, $c, 1;
                $c = ord($c);
                $size += ($c & 0x7f) << $shift;
                $shift += 7;
        }
        my $base_sha1;
        if ($$type == $PACK_TYPE{OBJ_DELTA}) {
                read FH, $base_sha1, 20;
                $base_sha1 = unpack("H40",$base_sha1);
        }
        my $x = inflateInit() or die "Cannot create inflation object";
        my ($input, $output, $status);
        while (read(FH, $input, 4096)) {
                ($output, $status) = $x->inflate(\$input);
                $buffer .= $output if $status == Z_OK || $status == Z_STREAM_END;
                last if $status != Z_OK;
        }
        close FH;
        die "read_packed_sha1: inflation failed\n" unless $status == Z_STREAM_END;
        die "data length unmatched: $size" unless length($buffer) == $size;
        return $buffer unless $$type == $PACK_TYPE{OBJ_DELTA};
        die "failed to find delta-pack base object"
                unless defined($packed_sha1{$base_sha1}) &&
                        $packed_sha1{$base_sha1}->{filename} eq $packed_sha1{$sha1}->{filename};
        my $base = read_packed_sha1 ($base_sha1, $type);
        my $b = patch_delta ($base, $buffer);
        die "Patch failed\n" unless defined ($b);
	cache_add($b, $sha1, $$type);
        $b;
}

sub find_sha1_file {
        my ($sha1) = @_;

        #TODO no altdb support
        -f "$git_object_dir/".substr($sha1,0,2)."/".substr($sha1,2,38);
}

sub has_sha1_file {
        my ($sha1) = @_;
        return 1 if find_pack_entry($sha1);
        find_sha1_file($sha1);
}

%delta_queue = ();
sub add_delta_to_list {
        my ($base_sha1, $delta_data, $pos) = @_;
        $delta_queue{$base_sha1} = [] unless defined $delta_queue{$base_sha1};
        push @{$delta_queue{$base_sha1}}, {"delta" => $delta_data, "pos" => $pos};
        #print STDERR "Queued $base_sha1\n";
}

sub read_sha1_file {
        my ($sha, $type) = @_;
        return read_packed_sha1($sha, $type)
                if find_pack_entry($sha);

        open BLOB, "<$git_object_dir/".substr($sha,0,2)."/".substr($sha,2,38)
                or die "Cannot open $sha\n";
        binmode BLOB;

        # uncompress the header
        my $x = inflateInit() or die "Cannot create inflation object\n";
        my $input = '';
        my ($data,$header,$output,$status);
        while (read(BLOB,$input,4096)) {
                ($output, $status) = $x->inflate(\$input) ;
                $header .= $output
                        if  $status == Z_OK or $status == Z_STREAM_END ;
                last if $status != Z_OK;
        }
        close BLOB;
        die "$sha: blob header inflation failed\n" unless $status == Z_STREAM_END;
        
        #parse the header
        die "invalid blob"
                unless $header =~ m/^([^ ]*) ([1-9][0-9]*)\0/;

        my $size = $2;
        my $header_len = length($&);
        if ($1 eq "commit") {
                $$type = 1;
        } elsif ($1 eq "tree") {
                $$type = 2;
        } elsif ($1 eq "blob") {
                $$type = 3;
        } elsif ($1 eq "tag") {
                $$type = 4;
        } else {
                die "invalid blob type $1\n";
        }

        die "blob length unmatch $size" unless $size == length($header) - $header_len;
        substr($header, $header_len);
}

sub patch_delta {
        my ($base, $delta) = @_;
        my $DELTA_SIZE_MIN = 4;

        return undef if length($delta) < $DELTA_SIZE_MIN;
        
        my $output = '';
        my $i = 0;
        my $cmd;
        my $size = 0;
        my $shift = 0;

        do {
                $cmd = ord(substr($delta,$i++,1));
                $size |= ($cmd & ~0x80) << $shift;
                $shift += 7;
        } while ($cmd & 0x80 && $i < length($delta));
        return undef if $size != length($base);

        $shift = 0;
        $size = 0;
        do {
                $cmd = ord(substr($delta,$i++,1));
                $size |= ($cmd & ~0x80) << $shift;
                $shift += 7;
        } while ($cmd & 0x80 && $i < length($delta));

        while ($i < length($delta)) {
                $cmd = ord(substr($delta,$i++,1));
                if ($cmd & 0x80) {
                        my $cp_off = 0;
                        my $cp_size = 0;
                        $cp_off = ord(substr($delta,$i++,1)) if $cmd & 0x01;
                        $cp_off |= ord(substr($delta,$i++,1)) << 8 if $cmd & 0x02;
                        $cp_off |= ord(substr($delta,$i++,1)) << 16 if $cmd & 0x04;
                        $cp_off |= ord(substr($delta,$i++,1)) << 24 if $cmd & 0x08;

                        $cp_size = ord(substr($delta,$i++,1)) if $cmd & 0x10;
                        $cp_size |= ord(substr($delta,$i++,1)) << 8 if $cmd & 0x20;
                        $cp_size |= ord(substr($delta,$i++,1)) << 16 if $cmd & 0x40;
                        $cp_size = 0x10000 if $cp_size == 0;
                        $output .= substr($base,$cp_off, $cp_size);
                } elsif ($cmd) {
                        $output .= substr($delta, $i, $cmd);
                        $i += $cmd;
                } else {
                        print STDERR "BAAAA\n";
                        return undef;
                }
        }
        length($output) == $size ? $output : undef;
}

sub resolve_delta {
        my ($base, $delta) = @_;
        patch_delta ($base, $delta);
}

sub unpack_delta_entry {
        my ($size,$type, $pos) = @_;

        my $base_sha1 = unpack("H40",fill_and_throw(20));

        my $delta_data = get_data($size);

        if (!has_sha1_file($base_sha1)) {
                add_delta_to_list ($base_sha1, $delta_data, $pos);
                return undef;
        }

        my $base = read_sha1_file($base_sha1, $type);
        die "failed to read delta-pack base object $base_sha1\n"
                unless defined($base);

        my $buffer = resolve_delta($base, $delta_data);
        $buffer;
}

sub object_added {
        my ($type, $buffer, $sha1, $pos) = @_;
        
	$packed_sha1{$sha1} = {"filename" => $write_pack, "pos" => $pos}
		if defined($pos) && defined($write_pack);
	$pack_blobs{$sha1} = $pos if defined($pos);

        if (defined($delta_queue{$sha1})) {
                foreach $delta (@{$delta_queue{$sha1}}) {
                        my $new_buffer = resolve_delta($buffer, $delta->{delta});
                        if ($keep_packs) {
                                my $sha = Digest::SHA1->new();
                                $sha->add($PACK_SIG{$type}." ".length($new_buffer)."\0");
                                $sha->add($new_buffer);
                                $sha1 = $sha->hexdigest();
                                object_added ($type, $new_buffer, $sha1, $delta->{pos});
                        } else {
                                write_object ($type, $new_buffer, $delta->{pos});
                        }
                }
#print STDERR "Unqueued $sha1\n";
                undef $delta_queue{$sha1};
        }
}

sub write_object {
        my ($type, $buffer, $pos) = @_;

        my $header = $PACK_SIG{$type}." ".length($buffer)."\0";
        my $sha = Digest::SHA1->new();
        $sha->add($header);
        $sha->add($buffer);
        my $sha1 = $sha->hexdigest();

        my $blob_filename = "$git_object_dir/".substr($sha1,0,2)."/".substr($sha1,2,38);
        if ( ! -f $blob_filename) {
        #if (1) {
                my $x = deflateInit()
                        or die "Cannot create deflation object\n";
                my $output2;
                my ($output, $status) = $x->deflate($header.$buffer);
                $status == Z_OK or die "deflation failed\n";
                $output2 = $output;
                ($output, $status) = $x->flush();
                $status == Z_OK or die "deflation failed\n";
                $output2 .= $output;
                safe_create_leading_directories ($blob_filename);
                if (!open BLOB, ">$blob_filename") {
                        die "Can't write to $blob_filename\n";
                }
                binmode BLOB;
                print BLOB $output2;
                close BLOB;

                #print STDERR "Wrote $sha1\n";
                object_added ($type, $buffer, $sha1, $pos);
        }
        $sha1;
}

sub unpack_one {
        my ($nr, $total) = @_;
        my $pos = $pack_count;
        my $c = fill_and_throw(1);
        $c = ord($c);
        my $type = ($c >> 4) & 7;
        my $size = ($c & 15);
        my $shift = 4;
        while ($c & 0x80) {
                $c = fill_and_throw(1);
                $c = ord($c);
                $size += ($c & 0x7f) << $shift;
                $shift += 7;
        }
        if (!defined($quiet)) {
                $last_sec = 0 if ! defined ($last_sec);
                $last_percent = 0 if ! defined ($last_percent);
                my $percentage = ($nr * 100) / $total;
                my $now = time();
                if ($percentage != $last_percent || $now != $last_sec) {
                        $last_sec = $now;
                        $last_percent = $percentage;
                        printf STDERR "%4u%% (%u/%u) done\r", $percentage, $nr, $total;
                }
        }
        my $buffer;
        if ($type == $PACK_TYPE{OBJ_COMMIT} ||
                $type == $PACK_TYPE{OBJ_TREE} ||
                $type == $PACK_TYPE{OBJ_BLOB} ||
                $type == $PACK_TYPE{OBJ_TAG}) {
                $buffer = unpack_non_delta_entry($type,$size);
        } elsif ($type == $PACK_TYPE{OBJ_DELTA}) {
                $buffer = unpack_delta_entry($size,\$type, $pos);
        } else {
                die "bad object type $type\n";
        }

        if (defined($buffer)) {
                my $sha1;
                if ($keep_packs) {
                        my $sha = Digest::SHA1->new();
                        $sha->add($PACK_SIG{$type}." ".length($buffer)."\0");
                        $sha->add($buffer);
                        $sha1 = $sha->hexdigest();
                        object_added ($type, $buffer, $sha1, $pos);
                } else {
                        $sha1 = write_object($type,$buffer, $pos);
                }
        }
}

sub process_pack {
        $pack_sha = Digest::SHA1->new();
        my $signature = fill_and_throw(4);
        die "not a pack. Wrong signature: $signature\n" unless $signature =~ m/^PACK/;

        my $version = fill_and_throw(4);
        $version = unpack("N",$version);
        die "unknown pack file version $version" unless $version == 2 || $version == 3;

        my $nr_obj = fill_and_throw(4);
        $nr_obj = unpack("N",$nr_obj);

        for ($i = 0;$i < $nr_obj; $i ++) {
                unpack_one($i+1, $nr_obj);
        }

        my $digest_sha = $pack_sha->hexdigest();
        my $sha = unpack("H40",fill_and_throw(20));
        die "final sha did not match\n" unless $sha eq $digest_sha;
        $sha;
}

sub show_ref {
        my ($path, $sha1) = @_;

        packet_write(1, $capabilities_sent ? "$sha1 $path\n" : "$sha1 $path\0$capabilities\n");
        $capabilities_sent = 1;
        0;
}

sub do_for_each_ref {
        my ($base) = @_;
        my $sha1;

        opendir(DIR,"$git_dir/$base") || die $!;
        $base =~ s,/*$,/,;
        foreach $dir (readdir(DIR)) {
                next if ($dir =~ /^\./ || length($dir) > 255 || $dir =~ /\.lock$/);
                my $path = $base.$dir;
                if (-d "$git_dir/$path") {
                        last if do_for_each_ref($path);
                        next;
                }
                if (read_ref("$git_dir/$path",\$sha1) < 0) {
                        print STDERR "$path points nowhere!\n";
                        next;
                }
                if (!has_sha1_file($sha1)) {
                        print STDERR "$path does not point to a valid commit object!\n";
                        next;
                }
                last if show_ref($path,$sha1);
        }
        closedir DIR;
        0;
}

sub write_head_info {
        do_for_each_ref("refs");
        show_ref("capabilities^{}", "0000000000000000000000000000000000000000") 
                unless $capabilities_sent;
}

sub index_pack {
        my ($sha1,$indices) = @_;

        $sha = Digest::SHA1->new();
        my $sha_buf;
        my $idx_filename = "$git_object_dir/pack/pack-$sha1.idx";
        open IDX, ">$idx_filename"
                        or die "Cannot create file $idx_filename\n";
        binmode IDX;
        my @sha1s = sort(keys(%{$indices}));
        my ($sha_base, $sha_count, @fanout);
        foreach $sha (@sha1s) {
                $sha_base = substr($sha,0,2) unless defined($sha_base);
                if ($sha_base ne substr($sha,0,2)) {
                        @fanout[hex($sha_base)] = $sha_count;
                        $sha_base = substr($sha,0,2);
                }
                $sha_count ++;
        }
        @fanout[hex($sha_base)] = $sha_count;
	$sha_count = 0;
        for ($i = 0;$i < 256; $i ++) {
		$sha_count = $fanout[$i] if defined($fanout[$i]);
                $sha_buf .= pack("N",$sha_count);
        }
        $sha->add($sha_buf);
        print IDX $sha_buf;
        $sha_buf = '';
        foreach $i (@sha1s) {
                $sha_buf = pack("N", ${$indices}{$i}).pack("H40", $i);
                $sha->add($sha_buf);
                print IDX $sha_buf;
        }
        $sha_buf = pack("H40", $sha1);
        $sha->add($sha_buf);
        print IDX $sha_buf;
        print IDX pack("H40", $sha->hexdigest());
        close IDX;
}

sub upload_pack {
        if ($keep_packs) {
                my $basename = ".pack-".time();
                my $pack_filename = "$git_object_dir/pack/$basename.pack";
                open PACK, ">$pack_filename"
                        or die "Cannot create file $pack_filename\n";
                binmode PACK;
		PACK->autoflush(1);

                $write_pack = $pack_filename;
                $sha = process_pack();
                close PACK;
                my $new_pack_filename = "$git_object_dir/pack/pack-$sha.pack";
                if (!rename $pack_filename, $new_pack_filename) {
			unlink $pack_filename;
			die "Cannot rename $pack_filename to $new_pack_filename\n";
		}
                        
                index_pack($sha,\%pack_blobs);
                prepare_packed_git;
        } else {
                process_pack();
        }
        0;
}

sub bad_ref_char {
        1;
}

sub verify_old_ref {
        my ($name, $hex) = @_;
        return 0 if $hex eq "0000000000000000000000000000000000000000";
        open LOCK, "<$name" or return -1;
        my $buf;
        my $ret = read LOCK, $buf, 40;
        close LOCK;
        return -1 if $ret != 40;
        return -1 unless $buf eq $hex;
        0;
}

sub adjust_shared_perm {
        0;
}

sub safe_create_leading_directories {
        my ($path) = @_;

        my @path = split("/", $path);
        shift @path if $path[0] eq "/";
        pop @path;
        my $pp = '';
        foreach $p (@path) {
                $pp .= '/' unless $pp eq '';
                $pp .= $p;

                if ( -e $pp) {
                        return -3 if (! -d $pp);
                } elsif (!mkdir($pp)) {
                        return -1;
                } elsif (adjust_shared_perm($pp)) {
                        return -2;
                }
        }
        0;
}

sub update {
        my ($cmd) = @_;

        $cmd->{error_string} = undef;
        if ($cmd->{ref_name} =~ m,^refs/, && !bad_ref_char($cmd->{ref_name})) {
                $cmd->{error_string} = "funny refname";
                print STDERR "refusing to create funny ref '$cmd->{ref_name}' locally\n";
                return 0;
        }
        my $lock_name = $cmd->{ref_name}.".lock";
        if (!has_sha1_file ($cmd->{new_sha1})) {
                $cmd->{error_string} = "bad pack";
                print STDERR "unpack should have generated $cmd->{new_sha1} but I can't find it!\n";
                return 0;
        }
        safe_create_leading_directories($lock_name);
        if (!open LOCK, ">$lock_name") {
                $cmd->error_string = "can't lock";
                print STDERR "unable to create $lock_name ($!)\n";
                return 0;
        }
        $result = print LOCK $cmd->{new_sha1}."\n";
        close LOCK;
        if (!$result) {
                unlink ($lock_name);
                $cmd->{error_string} = "can't write";
                print STDERR "unable to write $lock_name\n";
                return 0;
        }
        if (verify_old_ref($cmd->{ref_name}, $cmd->{old_sha1}) < 0) {
                unlink ($lock_name);
                $cmd->{error_string} = "raced";
                print STDERR "$cmd->{ref_name} changed during push\n";
                return 0;
        }
        if (!rename($lock_name, $cmd->{ref_name})) {
                unlink ($lock_name);
                $cmd->{error_string} = "can't rename";
                print STDERR "unable to replace $cmd->{ref_name}\n";
                return 0;
        } else {
                print STDERR "$cmd->{ref_name}: $cmd->{old_sha1} -> $cmd->{new_sha1}\n";
                return 0;
        }
}

sub execute_commands {
        foreach $cmd (@commands) {
                update $cmd;
        }
        #run_update_post_hook
}

sub report {
        my ($status) = @_;

        packet_write (1, $status ? "unpack $status\n" : "unpack ok\n");
        foreach $cmd (@commands) {
                if (${$cmd}{'error_string'}) {
                        packet_write (1, "ng ".${$cmd}{'ref_name'}." ".${$cmd}{'error_string'}."\n");
                } else {
                        packet_write (1, "ok ".${$cmd}{'ref_name'}."\n");
                }
        }
        packet_flush (1);
}

sub enter_repo {
        my ($path,$strict) = @_;
        my ($used_path, $validate_path);

        return 0 if !defined ($path);

        if (!$strict) {
                $path =~ s/^~/$ENV{"HOME"}/e;
                foreach $suffix ('.git/.git','/.git','.git','') {
                        if ( -d $path.$suffix ) {
                                $path .= $suffix;
                                last;
                        }
                }
        }

        chdir $path or return 0;

        if (-d "objects" && -d "refs" && !validate_symref("HEAD")) {
                $ENV{"GIT_DIR"} = ".";
                setup_git_env;
                prepare_packed_git;
                check_repository_format;
                return $path;
        }
        0;
}

sub update_info_refs {
        my $sha1;

        my $ref_file = "$git_dir/info/refs";
        safe_create_leading_directories($ref_file);
        if (!open REFINFO, ">$ref_file+") {
                print STDERR "unable to update $ref_file+\n";
                return 0;
        }
        my @path = ("refs");
        while ($#path >= 0) {
                my $path = shift @path;
                opendir(DIR,"$git_dir/$path") || next;
                foreach $dir (readdir(DIR)) {
                        next if ($dir =~ /^\./ || length($dir) > 255 || $dir =~ /\.lock$/);
                        if (-d "$git_dir/$path/$dir") {
                                push @path, "$path/$dir";
                                next;
                        }
                        if (read_ref("$git_dir/$path/$dir",\$sha1) < 0) {
                                print STDERR "$path/$dir points nowhere!\n";
                                next;
                        }
                        if (!has_sha1_file($sha1)) {
                                print STDERR "$path/$dir does not point to a valid commit object!\n";
                                next;
                        }
                        print REFINFO "$sha1\t$path/$dir\n";
                        my ($buffer, $type, $tag_sha1);
                        $tag_sha1 = $sha1;
                        while (1) {
                                $buffer = read_sha1_file($tag_sha1, \$type);
                                last if ($type != $PACK_TYPE{OBJ_TAG});
                                $tag_sha1 = $1 if $buffer =~ m/^object ([0-9a-fA-F]{40})/;
                        } 
                        print REFINFO "$tag_sha1\t$path/$dir^{}\n"
                                unless $tag_sha1 eq $sha1;
                }
                closedir DIR;
        }
        close REFINFO;
        rename "$ref_file+", $ref_file;
        0;
}

sub update_info_packs {
        my ($force) = @_;

        $outfile = "$git_object_dir/info/packs";

        safe_create_leading_directories($outfile);

        if (!open REFINFO, ">$outfile+") {
                print STDERR "Cannot write to $outfile+\n";
                return 0;
        }
        while (<$git_object_dir/pack/*.pack>) {
                print REFINFO "P $1\n"
                        if m,/(pack-[0-9a-fA-F]{40}.pack)$,;
        }
        print REFINFO "\n";
        close REFINFO;
        rename "$outfile+", "$outfile";
}

# main entry

$quiet = 1 unless -t STDERR;

$| = 1;

setup_git_env;
prepare_packed_git;

if ($0 =~ m/receive-pack(\.pl)?/) {
        foreach $arg (@ARGV) {
                usage() if $arg =~ m/^-/;
                usage() if defined($dir);
                $dir = $arg;
        }

        usage() if !defined($dir);

        enter_repo($dir, 0) or die "'$dir': unable to chdir or not a git archive\n";

        write_head_info;

        packet_flush(1);

        read_head_info;

        if ($#commands >= 0) {
                my $unpack_status = upload_pack;
                execute_commands if !$unpack_status;
                report($unpack_status) if $report_status;
        }

        update_info_refs();#if -e "$git_dir/info/refs";
        update_info_packs();# if -e "$git_object_dir/info/packs";
        exit 0;
}

if ($0 =~ m/unpack-objects(\.pl)?/) {
        $sha = process_pack;
        print STDERR "unpacked $sha\n";
        foreach $sha (sort(keys %pack_blobs)) {
                print pack("N",$pack_blobs{$sha});
                print pack("H40",$sha);
        }
        exit 0;
}

if ($0 =~ m/cat-file(.pl)?/) {
        my ($buffer,$type);
        $buffer = read_sha1_file($ARGV[0],\$type);
        print $PACK_SIG{$type}." - ".length($buffer)."\n";
        print $buffer;
        exit 0;
}

if ($0 =~ m/update-server-info(.pl)?/) {
        my $force = 0;
        foreach $arg (@ARGV) {
                if ($arg =~ m/--f(orce)?/) {
                        $force = 1;
                } else {
                        print STDERR "Usage: git-update-server-info [--force]\n";
                        exit 0;
                }
        }
        update_info_refs($force);
        update_info_packs($force);
        unlink("$git_dir/info/rev-cache");
        exit 0;
}

if ($0 =~ /index-pack(.pl)?/) {
        $keep_packs = 1; # so it won't write anything
        $sha = process_pack();
        index_pack($sha,\%pack_blobs);
        exit 0;
}

print STDERR "Unknown command $0\n";


Cập nhật 3 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | Git

Thứ hai, 19 Tháng sáu năm 2006 01:14:25 ICT

Giết! Giết! Giết!

git push nguyên kho git, chạy mát trời (hơi chậm). Nhưng cái index phát sinh hình như bị thiếu hết phân nửa :( Có thể là do mấy cái delta object. Bố khỉ, còn có tí xíu.

#!/usr/bin/perl -w

use Compress::Zlib;
use Digest::SHA1;

$report_status = 0;
$capabilities = "report-status";
$capabilities_sent = 0;
$keep_packs = 1;
$write_pack = 0;

sub usage {
	die "git-receive-pack.pl <git-dir>";
}

sub validate_symref {
	my ($path) = @_;

	if (-l $path) {
		my $real_path = readlink $path;
		return $real_path =~ m,^refs/, ? 0 : -1;
	}
	open FH, $path or return -1;
	my $line = <FH>;
	close FH;
	$line =~ m,^ref: *refs/., ? 0 : -1;
}

sub check_repository_format {
	#TODO
	return 0;
}

sub setup_git_env {
	$git_dir = $ENV{"GIT_DIR"};
	$git_dir = '.git' if !defined($git_dir);

	$git_object_dir = $ENV{"GIT_OBJECT_DIRECTORY"};
	$git_object_dir = "$git_dir/objects"
		unless defined($git_object_dir);

	#$git_refs_dir = "$git_dir/refs";

	$git_index_file = $ENV{"GIT_INDEX_FILE"};
	$git_index_file = "$git_dir/index"
		unless defined($git_index_file);

	$git_graft_file = $ENV{"GIT_GRAFT_FILE"};
	$git_graft_file = "$git_dir/info/grafts"
		unless defined($git_graft_file);
}

sub resolve_ref {
	my ($path, $sha1, $reading) = @_;
	my $depth = 5; #MAXDEPTH
	my $len;

	while (1) {
		return 0 if --$depth < 0;

		if (! -f $path) {
			return 0 if $reading || !defined($!{ENOENT});
			$$sha1 = "0000000000000000000000000000000000000000";
			return $path;
		}

		if (-l $path) {
			my $real_path = readlink ($path);
			if ($real_path =~ m,^refs/,) {
				$path = "$git_dir/$real_path";
				next;
			}
		}

		open FH, $path or return 0;
		my $line = <FH>;
		close FH;

		chomp($line);
		if ($line =~ m/^ref: *(.*)$/) {
			$path = "$git_dir/$1";
			next;
		}

		$$sha1 = $line;
		return $path;
	}
	return $path;
}

sub read_ref {
	my ($filename, $sha1) = @_;
	resolve_ref($filename, $sha1, 1) ? 0 : -1;
}

sub packet_write {
	my ($fd, $text) = @_;

	my $len = length($text)+4;
	#printf STDERR "packet_write: %d %04x %s", $len, $len, $text;
	printf "%04x%s", $len, $text;
}

sub packet_flush {
	my ($fd) = @_;

	print "0000";
	#print STDERR "packet_flush\n";
}


@commands = ();

sub read_head_info {
	while (1) {
		my $len;
		my $ret = read(STDIN, $len, 4);
		die "packet_read error: $!\n" if !defined($ret) || $ret != 4;
		die "protocol error: bad line length character: <$len>" if $len !~ m/^[0-9a-fA-F]{4}$/;
		$len = hex($len);
		last if !$len;
		$len -= 4;
		my $line;
		$ret = read(STDIN, $line, $len);
		die "packet_read error: $!\n" if !defined($ret) || $ret != $len;
		die "protocol error: bad line length $len" if $len != length($line);
		#print STDERR "packet_read: $line\n";
		die "protocol error: expected old/new/ref, got $line" 
			if $line !~ /^([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) ([^\0]+)(\0report-status)?$/;

		$report_status = 1 if defined($4);

		my $cmd = {
			'old_sha1' => $1,
			'new_sha1' => $2,
			'ref_name' => $3,
			'error_string' => 0,
		};
		$commands[$#commands+1] = $cmd;
	}
}

%PACK_TYPE = (
	OBJ_EXT => 0,
	OBJ_COMMIT => 1,
	OBJ_TREE => 2,
	OBJ_BLOB => 3,
	OBJ_TAG => 4,
	OBJ_DELTA => 7
);

%PACK_SIG = ( 1 => "commit", 2 => "tree", 3 => "blob", 4 => "tag" );

$pack_buffer = '';
$pack_count = 0;

sub fill {
	my ($min) = @_;

	if ($min > length($pack_buffer)) {
		my $buf;
		read(STDIN, $buf, 4096);
		$pack_buffer .= $buf;
	}
	substr($pack_buffer, 0, $min);
}

sub throw_away {
	my ($len) = @_;
	my $data = substr($pack_buffer, 0, $len);
	$pack_sha->add($data);
	print PACK $data if $keep_packs and $write_pack;
	$pack_buffer = substr($pack_buffer, $len);
	$pack_count += $len;
}

sub fill_and_throw {
	my ($len) = @_;
	$a = fill($len);
	throw_away($len);
	$a;
}

%pack_blobs = ();

sub get_data {
	my ($size) = @_;

	my $x = inflateInit() or die "Cannot create a inflation stream\n";
	my $buffer = '';
	my ($output, $status, $c);
	while (1) {
		fill(1);
		$c = $pack_buffer;
		($output, $status) = $x->inflate(\$c);
		throw_away(length($pack_buffer)-length($c));
		$buffer .= $output if $status == Z_OK or $status == Z_STREAM_END;
		last if $status != Z_OK;
	}
	die "inflation failed\n" unless $status == Z_STREAM_END && length($buffer) == $size;
	$buffer;
}

sub unpack_non_delta_entry {
	my ($type, $size) = @_;

	my $buffer = get_data($size);
	$buffer;
	#print STDERR "Got $type\n";
}

sub prepare_packed_git {
	%packed_sha1 = ();
	while (<$git_object_dir/pack/*.pack>) {
		my $pack_file = $_;
		my $idx_file = $pack_file;
		$idx_file =~ s/\.pack$/.idx/;
		next unless -e $idx_file;

		open IDX, "<$idx_file" or next;
		binmode IDX;
		my $buf;

		read IDX,$buf,4*256;
		my $count = unpack("N",substr($buf,4*255));
		my $blen = read(IDX,$buf,(4+20)*$count);
		my @idx = unpack("(NH40)$count",$buf);
		for ($i = 0;$i < $count;$i ++) {
			$packed_sha1{$idx[$i*2+1]} = {"filename" => $pack_file, "pos" => $idx[$i*2]};
		}
	}
}

sub find_pack_entry {
	my ($sha1) = @_;
	defined($packed_sha1{$sha1});
}

sub read_packed_sha1 {
	my ($sha1, $type) = @_;

	open FH, $packed_sha1{$sha1}->{filename}
		or die "Cannot open pack ".$packed_sha1{$sha1}->{filename}.".\n";
	binmode FH;
	my $c;
	seek FH, $packed_sha1{$sha1}->{pos}, 0;
	read FH, $c, 1;
	$c = ord($c);
	$$type = ($c >> 4) & 7;
	my $size = ($c & 15);
	my $shift = 4;
	while ($c & 0x80) {
		read FH, $c, 1;
		$c = ord($c);
		$size += ($c & 0x7f) << $shift;
		$shift += 7;
	}
	my $base_sha1;
	if ($$type == $PACK_TYPE{OBJ_DELTA}) {
		read FH, $base_sha1, 20;
		$base_sha1 = unpack("H40",$base_sha1);
	}
	my $x = inflateInit() or die "Cannot create inflation object";
	my ($buffer, $input, $output, $status);
	while (read(FH, $input, 4096)) {
		($output, $status) = $x->inflate(\$input);
		$buffer .= $output if $status == Z_OK || $status == Z_STREAM_END;
		last if $status != Z_OK;
	}
	close FH;
	die "read_packed_sha1: inflation failed\n" unless $status == Z_STREAM_END;
	die "data length unmatched: $size" unless length($buffer) == $size;
	return $buffer unless $$type == $PACK_TYPE{OBJ_DELTA};
	die "failed to find delta-pack base object"
		unless defined($packed_sha1{$base_sha1}) &&
			$packed_sha1{$base_sha1}->{filename} eq $packed_sha1{$sha1}->{filename};
	my $base = read_packed_sha1 ($base_sha1, $type);
	my $b = patch_delta ($base, $buffer);
	die "Patch failed\n" unless defined ($b);
	$b;
}

sub find_sha1_file {
	my ($sha1) = @_;

	#TODO no altdb support
	-f "$git_object_dir/".substr($sha1,0,2)."/".substr($sha1,2,38);
}

sub has_sha1_file {
	my ($sha1) = @_;
	return 1 if find_pack_entry($sha1);
	find_sha1_file($sha1);
}

%delta_queue = ();
sub add_delta_to_list {
	my ($base_sha1, $delta_data, $size) = @_;
	$delta_queue{$base_sha1} = [] unless defined $delta_queue{$base_sha1};
	push @{$delta_queue{$base_sha1}}, $delta_data;
	#print STDERR "Queued $base_sha1\n";
}

sub read_sha1_file {
	my ($sha, $type) = @_;
	return read_packed_sha1($sha, $type)
		if find_pack_entry($sha);

	open BLOB, "<$git_object_dir/".substr($sha,0,2)."/".substr($sha,2,38)
		or die "Cannot open $sha\n";
	binmode BLOB;

	# uncompress the header
	my $x = inflateInit() or die "Cannot create inflation object\n";
	my $input = '';
	my ($data,$header,$output,$status);
	while (read(BLOB,$input,4096)) {
		($output, $status) = $x->inflate(\$input) ;
		$header .= $output
			if  $status == Z_OK or $status == Z_STREAM_END ;
		last if $status != Z_OK;
	}
	close BLOB;
	die "$sha: blob header inflation failed\n" unless $status == Z_STREAM_END;
	
	#parse the header
	die "invalid blob"
		unless $header =~ m/^([^ ]*) ([1-9][0-9]*)\0/;

	my $size = $2;
	my $header_len = length($&);
	if ($1 eq "commit") {
		$$type = 1;
	} elsif ($1 eq "tree") {
		$$type = 2;
	} elsif ($1 eq "blob") {
		$$type = 3;
	} elsif ($1 eq "tag") {
		$$type = 4;
	} else {
		die "invalid blob type $1\n";
	}

	die "blob length unmatch $size" unless $size == length($header) - $header_len;
	substr($header, $header_len);
}

sub patch_delta {
	my ($base, $delta) = @_;
	my $DELTA_SIZE_MIN = 4;

	return undef if length($delta) < $DELTA_SIZE_MIN;
	
	my $output = '';
	my $i = 0;
	my $cmd;
	my $size = 0;
	my $shift = 0;

	do {
		$cmd = ord(substr($delta,$i++,1));
		$size |= ($cmd & ~0x80) << $shift;
		$shift += 7;
	} while ($cmd & 0x80 && $i < length($delta));
	return undef if $size != length($base);

	$shift = 0;
	$size = 0;
	do {
		$cmd = ord(substr($delta,$i++,1));
		$size |= ($cmd & ~0x80) << $shift;
		$shift += 7;
	} while ($cmd & 0x80 && $i < length($delta));

	while ($i < length($delta)) {
		$cmd = ord(substr($delta,$i++,1));
		if ($cmd & 0x80) {
			my $cp_off = 0;
			my $cp_size = 0;
			$cp_off = ord(substr($delta,$i++,1)) if $cmd & 0x01;
			$cp_off |= ord(substr($delta,$i++,1)) << 8 if $cmd & 0x02;
			$cp_off |= ord(substr($delta,$i++,1)) << 16 if $cmd & 0x04;
			$cp_off |= ord(substr($delta,$i++,1)) << 24 if $cmd & 0x08;

			$cp_size = ord(substr($delta,$i++,1)) if $cmd & 0x10;
			$cp_size |= ord(substr($delta,$i++,1)) << 8 if $cmd & 0x20;
			$cp_size |= ord(substr($delta,$i++,1)) << 16 if $cmd & 0x40;
			$cp_size = 0x10000 if $cp_size == 0;
			$output .= substr($base,$cp_off, $cp_size);
		} elsif ($cmd) {
			$output .= substr($delta, $i, $cmd);
			$i += $cmd;
		} else {
			print STDERR "BAAAA\n";
			return undef;
		}
	}
	length($output) == $size ? $output : undef;
}

sub resolve_delta {
	my ($base, $delta) = @_;
	patch_delta ($base, $delta);
}

sub unpack_delta_entry {
	my ($size,$type) = @_;

	my $base_sha1 = unpack("H40",fill_and_throw(20));

	my $delta_data = get_data($size);

	if (!has_sha1_file($base_sha1)) {
		add_delta_to_list ($base_sha1, $delta_data, $size);
		return undef;
	}

	my $base = read_sha1_file($base_sha1, $type);
	die "failed to read delta-pack base object $base_sha1\n"
		unless defined($base);

	my $buffer = resolve_delta($base, $delta_data);
	$buffer;
}

sub object_added {
	my ($type, $buffer, $sha1) = @_;
	
	if (defined($delta_queue{$sha1})) {
		foreach $delta (@{$delta_queue{$sha1}}) {
			my $new_buffer = resolve_delta($buffer, $delta);
			if ($keep_packs) {
				my $sha = Digest::SHA1->new();
				$sha->add($PACK_SIG{$type}." ".length($new_buffer)."\0");
				$sha->add($new_buffer);
				$sha1 = $sha->hexdigest();
				object_added ($type, $new_buffer, $sha1);
			} else {
				write_object ($type, $new_buffer);
			}
		}
#print STDERR "Unqueued $sha1\n";
		undef $delta_queue{$sha1};
	}
}

sub write_object {
	my ($type, $buffer) = @_;

	my $header = $PACK_SIG{$type}." ".length($buffer)."\0";
	my $sha = Digest::SHA1->new();
	$sha->add($header);
	$sha->add($buffer);
	my $sha1 = $sha->hexdigest();

	my $blob_filename = "$git_object_dir/".substr($sha1,0,2)."/".substr($sha1,2,38);
	if ( ! -f $blob_filename) {
	#if (1) {
		my $x = deflateInit()
			or die "Cannot create deflation object\n";
		my $output2;
		my ($output, $status) = $x->deflate($header.$buffer);
		$status == Z_OK or die "deflation failed\n";
		$output2 = $output;
		($output, $status) = $x->flush();
		$status == Z_OK or die "deflation failed\n";
		$output2 .= $output;
		safe_create_leading_directories ($blob_filename);
		if (!open BLOB, ">$blob_filename") {
			die "Can't write to $blob_filename\n";
		}
		binmode BLOB;
		print BLOB $output2;
		close BLOB;

		#print STDERR "Wrote $sha1\n";
		object_added ($type, $buffer, $sha1);
	}
	$sha1;
}

sub unpack_one {
	my ($nr, $total) = @_;
	my $pos = $pack_count;
	my $c = fill_and_throw(1);
	$c = ord($c);
	my $type = ($c >> 4) & 7;
	my $size = ($c & 15);
	my $shift = 4;
	while ($c & 0x80) {
		$c = fill_and_throw(1);
		$c = ord($c);
		$size += ($c & 0x7f) << $shift;
		$shift += 7;
	}
	if (!defined($quiet)) {
		$last_sec = 0 if ! defined ($last_sec);
		$last_percent = 0 if ! defined ($last_percent);
		my $percentage = ($nr * 100) / $total;
		my $now = time();
		if ($percentage != $last_percent || $now != $last_sec) {
			$last_sec = $now;
			$last_percent = $percentage;
			printf STDERR "%4u%% (%u/%u) done\r", $percentage, $nr, $total;
		}
	}
	my $buffer;
	if ($type == $PACK_TYPE{OBJ_COMMIT} ||
		$type == $PACK_TYPE{OBJ_TREE} ||
		$type == $PACK_TYPE{OBJ_BLOB} ||
		$type == $PACK_TYPE{OBJ_TAG}) {
		$buffer = unpack_non_delta_entry($type,$size);
	} elsif ($type == $PACK_TYPE{OBJ_DELTA}) {
		$buffer = unpack_delta_entry($size,\$type);
	} else {
		die "bad object type $type\n";
	}

	if (defined($buffer)) {
		my $sha1;
		if ($keep_packs) {
			my $sha = Digest::SHA1->new();
			$sha->add($PACK_SIG{$type}." ".length($buffer)."\0");
			$sha->add($buffer);
			$sha1 = $sha->hexdigest();
			object_added ($type, $buffer, $sha1);
		} else {
			$sha1 = write_object($type,$buffer);
		}
		$pack_blobs{$sha1} = $pos;
	}
}

sub process_pack {
	$pack_sha = Digest::SHA1->new();
	my $signature = fill_and_throw(4);
	die "not a pack. Wrong signature: $signature\n" unless $signature =~ m/^PACK/;

	my $version = fill_and_throw(4);
	$version = unpack("N",$version);
	die "unknown pack file version $version" unless $version == 2 || $version == 3;

	my $nr_obj = fill_and_throw(4);
	$nr_obj = unpack("N",$nr_obj);

	for ($i = 0;$i < $nr_obj; $i ++) {
		unpack_one($i+1, $nr_obj);
	}

	my $digest_sha = $pack_sha->hexdigest();
	my $sha = unpack("H40",fill_and_throw(20));
	die "final sha did not match\n" unless $sha eq $digest_sha;
	$sha;
}

sub show_ref {
	my ($path, $sha1) = @_;

	packet_write(1, $capabilities_sent ? "$sha1 $path\n" : "$sha1 $path\0$capabilities\n");
	$capabilities_sent = 1;
	0;
}

sub do_for_each_ref {
	my ($base) = @_;
	my $sha1;

	opendir(DIR,"$git_dir/$base") || die $!;
	$base =~ s,/*$,/,;
	foreach $dir (readdir(DIR)) {
		next if ($dir =~ /^\./ || length($dir) > 255 || $dir =~ /\.lock$/);
		my $path = $base.$dir;
		if (-d "$git_dir/$path") {
			last if do_for_each_ref($path);
			next;
		}
		if (read_ref("$git_dir/$path",\$sha1) < 0) {
			print STDERR "$path points nowhere!\n";
			next;
		}
		if (!has_sha1_file($sha1)) {
			print STDERR "$path does not point to a valid commit object!\n";
			next;
		}
		last if show_ref($path,$sha1);
	}
	closedir DIR;
	0;
}

sub write_head_info {
	do_for_each_ref("refs");
	show_ref("capabilities^{}", "0000000000000000000000000000000000000000") 
		unless $capabilities_sent;
}

sub index_pack {
	my ($sha1,$indices) = @_;

	$sha = Digest::SHA1->new();
	my $sha_buf;
	my $idx_filename = "$git_object_dir/pack/pack-$sha1.idx";
	open IDX, ">$idx_filename"
			or die "Cannot create file $idx_filename\n";
	binmode IDX;
	my @sha1s = sort(keys(%{$indices}));
	my ($sha_base, $sha_count, @fanout);
	foreach $sha (@sha1s) {
		$sha_base = substr($sha,0,2) unless defined($sha_base);
		if ($sha_base ne substr($sha,0,2)) {
			@fanout[hex($sha_base)] = $sha_count;
			$sha_base = substr($sha,0,2);
		}
		$sha_count ++;
	}
	@fanout[hex($sha_base)] = $sha_count;
	for ($i = 0;$i < 256; $i ++) {
		$sha_buf .= pack("N",defined($fanout[$i]) ? $fanout[$i] : 0);
	}
	$sha->add($sha_buf);
	print IDX $sha_buf;
	$sha_buf = '';
	foreach $i (@sha1s) {
		$sha_buf = pack("N", ${$indices}{$i}).pack("H40", $i);
		$sha->add($sha_buf);
		print IDX $sha_buf;
	}
	$sha_buf = pack("H40", $sha1);
	$sha->add($sha_buf);
	print IDX $sha_buf;
	print IDX pack("H40", $sha->hexdigest());
	close IDX;
}

sub upload_pack {
	if ($keep_packs) {
		my $basename = ".pack-".time();
		my $pack_filename = "$git_object_dir/pack/$basename.pack";
		open PACK, ">$pack_filename"
			or die "Cannot create file $pack_filename\n";
		binmode PACK;

		$write_pack = 1;
		$sha = process_pack();
		close PACK;
		my $new_pack_filename = "$git_object_dir/pack/pack-$sha.pack";
		rename $pack_filename, $new_pack_filename
			or die "Cannot rename $pack_filename to $new_pack_filename\n";
			
		index_pack($sha,\%pack_blobs);
		prepare_packed_git;
	} else {
		process_pack();
	}
	0;
}

sub bad_ref_char {
	1;
}

sub verify_old_ref {
	my ($name, $hex) = @_;
	return 0 if $hex eq "0000000000000000000000000000000000000000";
	open LOCK, "<$name" or return -1;
	my $buf;
	my $ret = read LOCK, $buf, 40;
	close LOCK;
	return -1 if $ret != 40;
	return -1 unless $buf eq $hex;
	0;
}

sub adjust_shared_perm {
	0;
}

sub safe_create_leading_directories {
	my ($path) = @_;

	my @path = split("/", $path);
	shift @path if $path[0] eq "/";
	pop @path;
	my $pp = '';
	foreach $p (@path) {
		$pp .= '/' unless $pp eq '';
		$pp .= $p;

		if ( -e $pp) {
			return -3 if (! -d $pp);
		} elsif (!mkdir($pp)) {
			return -1;
		} elsif (adjust_shared_perm($pp)) {
			return -2;
		}
	}
	0;
}

sub update {
	my ($cmd) = @_;

	$cmd->{error_string} = undef;
	if ($cmd->{ref_name} =~ m,^refs/, && !bad_ref_char($cmd->{ref_name})) {
		$cmd->{error_string} = "funny refname";
		print STDERR "refusing to create funny ref '$cmd->{ref_name}' locally\n";
		return 0;
	}
	my $lock_name = $cmd->{ref_name}.".lock";
	if (!has_sha1_file ($cmd->{new_sha1})) {
		$cmd->{error_string} = "bad pack";
		print STDERR "unpack should have generated $cmd->{new_sha1} but I can't find it!\n";
		return 0;
	}
	safe_create_leading_directories($lock_name);
	if (!open LOCK, ">$lock_name") {
		$cmd->error_string = "can't lock";
		print STDERR "unable to create $lock_name ($!)\n";
		return 0;
	}
	$result = print LOCK $cmd->{new_sha1}."\n";
	close LOCK;
	if (!$result) {
		unlink ($lock_name);
		$cmd->{error_string} = "can't write";
		print STDERR "unable to write $lock_name\n";
		return 0;
	}
	if (verify_old_ref($cmd->{ref_name}, $cmd->{old_sha1}) < 0) {
		unlink ($lock_name);
		$cmd->{error_string} = "raced";
		print STDERR "$cmd->{ref_name} changed during push\n";
		return 0;
	}
	if (!rename($lock_name, $cmd->{ref_name})) {
		unlink ($lock_name);
		$cmd->{error_string} = "can't rename";
		print STDERR "unable to replace $cmd->{ref_name}\n";
		return 0;
	} else {
		print STDERR "$cmd->{ref_name}: $cmd->{old_sha1} -> $cmd->{new_sha1}\n";
		return 0;
	}
}

sub execute_commands {
	foreach $cmd (@commands) {
		update $cmd;
	}
	#run_update_post_hook
}

sub report {
	my ($status) = @_;

	packet_write (1, $status ? "unpack $status\n" : "unpack ok\n");
	foreach $cmd (@commands) {
		if (${$cmd}{'error_string'}) {
			packet_write (1, "ng ".${$cmd}{'ref_name'}." ".${$cmd}{'error_string'}."\n");
		} else {
			packet_write (1, "ok ".${$cmd}{'ref_name'}."\n");
		}
	}
	packet_flush (1);
}

sub enter_repo {
	my ($path,$strict) = @_;
	my ($used_path, $validate_path);

	return 0 if !defined ($path);

	if (!$strict) {
		$path =~ s/^~/$ENV{"HOME"}/e;
		foreach $suffix ('.git/.git','/.git','.git','') {
			if ( -d $path.$suffix ) {
				$path .= $suffix;
				last;
			}
		}
	}

	chdir $path or return 0;

	if (-d "objects" && -d "refs" && !validate_symref("HEAD")) {
		$ENV{"GIT_DIR"} = ".";
		setup_git_env;
		prepare_packed_git;
		check_repository_format;
		return $path;
	}
	0;
}

sub update_info_refs {
	my $sha1;

	my $ref_file = "$git_dir/info/refs";
	safe_create_leading_directories($ref_file);
	if (!open REFINFO, ">$ref_file+") {
		print STDERR "unable to update $ref_file+\n";
		return 0;
	}
	my @path = ("refs");
	while ($#path >= 0) {
		my $path = shift @path;
		opendir(DIR,"$git_dir/$path") || next;
		foreach $dir (readdir(DIR)) {
			next if ($dir =~ /^\./ || length($dir) > 255 || $dir =~ /\.lock$/);
			if (-d "$git_dir/$path/$dir") {
				push @path, "$path/$dir";
				next;
			}
			if (read_ref("$git_dir/$path/$dir",\$sha1) < 0) {
				print STDERR "$path/$dir points nowhere!\n";
				next;
			}
			if (!has_sha1_file($sha1)) {
				print STDERR "$path/$dir does not point to a valid commit object!\n";
				next;
			}
			print REFINFO "$sha1	$path/$dir\n";
			my ($buffer, $type, $tag_sha1);
			$tag_sha1 = $sha1;
			while (1) {
				$buffer = read_sha1_file($tag_sha1, \$type);
				last if ($type != $PACK_TYPE{OBJ_TAG});
				$tag_sha1 = $1 if $buffer =~ m/^object ([0-9a-fA-F]{40})/;
			} 
			print REFINFO "$tag_sha1	$path/$dir^{}\n"
				unless $tag_sha1 eq $sha1;
		}
		closedir DIR;
	}
	close REFINFO;
	rename "$ref_file+", $ref_file;
	0;
}

sub update_info_packs {
	my ($force) = @_;

	$outfile = "$git_object_dir/info/packs";

	safe_create_leading_directories($outfile);

	if (!open REFINFO, ">$outfile+") {
		print STDERR "Cannot write to $outfile+\n";
		return 0;
	}
	while (<$git_object_dir/pack/*.pack>) {
		print REFINFO "P $1\n"
			if m,/pack-([0-9a-fA-F]{40}).pack$,;
	}
	print REFINFO "\n";
	close REFINFO;
	rename "$outfile+", "$outfile";
}

# main entry

$quiet = 1 unless -t STDERR;

$| = 1;

setup_git_env;
prepare_packed_git;

if ($0 =~ m/receive-pack(\.pl)?/) {
	foreach $arg (@ARGV) {
		usage() if $arg =~ m/^-/;
		usage() if defined($dir);
		$dir = $arg;
	}

	usage() if !defined($dir);

	enter_repo($dir, 0) or die "'$dir': unable to chdir or not a git archive\n";

	write_head_info;

	packet_flush(1);

	read_head_info;

	if ($#commands >= 0) {
		my $unpack_status = upload_pack;
		execute_commands if !$unpack_status;
		report($unpack_status) if $report_status;
	}

	update_info_refs();#if -e "$git_dir/info/refs";
	update_info_packs();# if -e "$git_object_dir/info/packs";
	exit 0;
}

if ($0 =~ m/unpack-objects(\.pl)?/) {
	$sha = process_pack;
	print STDERR "unpacked $sha\n";
	foreach $sha (sort(keys %pack_blobs)) {
		print pack("N",$pack_blobs{$sha});
		print pack("H40",$sha);
	}
	exit 0;
}

if ($0 =~ m/cat-file(.pl)?/) {
	my ($buffer,$type);
	$buffer = read_sha1_file($ARGV[0],\$type);
	print $PACK_SIG{$type}." - ".length($buffer)."\n";
	print $buffer;
	exit 0;
}

if ($0 =~ m/update-server-info(.pl)?/) {
	my $force = 0;
	foreach $arg (@ARGV) {
		if ($arg =~ m/--f(orce)?/) {
			$force = 1;
		} else {
			print STDERR "Usage: git-update-server-info [--force]\n";
			exit 0;
		}
	}
	update_info_refs($force);
	update_info_packs($force);
	unlink("$git_dir/info/rev-cache");
	exit 0;
}

if ($0 =~ /index-pack(.pl)?/) {
	$keep_packs = 1; # so it won't write anything
	$sha = process_pack();
	index_pack($sha,\%pack_blobs);
	exit 0;
}

print STDERR "Unknown command $0\n";


Cập nhật 3 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | Git

Chủ nhật, 18 Tháng sáu năm 2006 19:25:55 ICT

Git! Git! Git!

Cuối cùng receive-pack.pl cũng đã có thể unpack được (kể cả delta object). Chỉ còn một tẹo update-server-info và xử lý branch.

Mai là có thể push lên dev.gentoo.org rồi, chào em rsync!


Cập nhật 3 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | Git

Thứ bảy, 17 Tháng sáu năm 2006 23:55:40 ICT

receive-pack.pl

Mặc dù đã có thể thao tác với sftp tuy nhiên coi bộ xử lý mấy cái mớ lằng nhằng còn lại chua lè.

Giải pháp khác là viết lại receive-pack bằng perl vì đa số các hệ thống đều hỗ trợ perl khá tốt. receive-pack.pl cần có Digest::SHA1Compress::Zlib, ngoài ra vô tư. Tuy nhiên vẫn còn hai phần chưa xong

  • Viết phần cập nhật ref
  • Viết phần phát sinh index cho pack.

Phát sinh pack index là phần không chua cũng đắng vì phải phân tích pack (ok), phát sinh sha-1 cho từng blob trong đó. Với mấy blob thông thường thì ok. Nhưng blob delta thì cần phải có thuật toán để phát sinh blob mới từ delta, chưa kể phải đọc blob đã có. Thuật toán phát sinh blob có vẻ dễ (cỡ hai trang). Nhưng phần đọc blob thì te văn tua: nếu blob nằm trong pack thì đồng nghĩa với việc đọc pack (ok) đọc pack index (trong khi còn chưa tạo được pack index :( )

Tuy nhiên có vẻ cũng đáng viết, vì nếu đọc được blob thì giải quyết được nửa đoạn đường đưa gitweb lên server mà không cần các công cụ git bên dưới. gitweb dùng git-rev-parse, git-rev-list, git-ls-tree, git-cat-file và .. má ơi.. git-diff-tree. Phức tạp nhất có lẽ là git-rev-list và git-diff-tree.

#!/bin/env perl

use Compress::Zlib;
use Digest::SHA1;

$report_status = 0;
$capabilities = "report-status";
$capabilities_sent = 0;


sub usage {
	die "git-receive-pack.pl <git-dir>";
}

sub validate_symref {
	my ($path) = @_;

	if (-l $path) {
		my $real_path = readlink $path;
		return $real_path =~ m,^refs/, ? 0 : -1;
	}
	open FH, $path or return -1;
	my $line = <FH>;
	close FH;
	$line =~ m,^ref: *refs/., ? 0 : -1;
}

sub check_repository_format {
	#TODO
	return 0;
}

sub setup_git_env {
	$git_dir = $ENV{"GIT_DIR"};
	$git_dir = '.git' if !defined($git_dir);

	$git_object_dir = $ENV{"GIT_OBJECT_DIRECTORY"};
	$git_object_dir = "$git_dir/objects"
		unless defined($git_object_dir);

	$git_refs_dir = "$git_dir/refs";

	$git_index_file = $ENV{"GIT_INDEX_FILE"};
	$git_index_file = "$git_dir/index"
		unless defined($git_index_file);

	$git_graft_file = $ENV{"GIT_GRAFT_FILE"};
	$git_graft_file = "$git_dir/info/grafts"
		unless defined($git_graft_file);
}

sub enter_repo {
	my ($path,$strict) = @_;
	my $used_path, $validate_path;

	return 0 if !defined ($path);

	if (!$strict) {
		$path =~ s/^~/$ENV{"HOME"}/e;
		foreach $suffix ('.git/.git','/.git','.git','') {
			if ( -d $path.$suffix ) {
				$path .= $suffix;
				last;
			}
		}
	}

	chdir $path or return 0;

	if (-d "objects" && -d "refs" && !validate_symref("HEAD")) {
		$ENV{"GIT_DIR"} = ".";
		setup_git_env;
		check_repository_format;
		return $path;
	}
	0;
}

sub resolve_ref {
	my ($path, $sha1, $reading) = @_;
	my $depth = 5; #MAXDEPTH
	my $len;

	while (1) {
		return 0 if --$depth < 0;

		if (! -f $path) {
			return 0 if $reading || !defined($!{ENOENT});
			$$sha1 = "0000000000000000000000000000000000000000";
			return $path;
		}

		if (-l $path) {
			my $real_path = readlink ($path);
			if ($real_path =~ m,^refs/,) {
				$path = "$git_dir/$real_path";
				next;
			}
		}

		open FH, $path or return 0;
		my $line = <FH>;
		close FH;

		chomp($line);
		if ($line =~ m/^ref: *(.*)$/) {
			$path = "$git_dir/$1";
			next;
		}

		$$sha1 = $line;
		return $path;
	}
	return $path;
}

sub read_ref {
	my ($filename, $sha1) = @_;
	resolve_ref($filename, $sha1, 1) ? 0 : -1;
}

sub packet_write {
	my ($fd, $text) = @_;

	my $len = length($text)+4;
	#printf STDERR "packet_write: %d %04x %s", $len, $len, $text;
	printf "%04x%s", $len, $text;
}

sub packet_flush {
	my ($fd) = @_;

	print "0000";
	#print STDERR "packet_flush\n";
}

sub show_ref {
	my ($path, $sha1) = @_;

	packet_write(1, $capabilities_sent ? "$sha1 $path\n" : "$sha1 $path\0$capabilities\n");
	$capabilities_sent = 1;
	0;
}

sub do_for_each_ref {
	my ($base) = @_;
	my $sha1;

	opendir(DIR,"$git_dir/$base") || die $!;
	$base =~ s,/*$,/,;
	foreach $dir (readdir(DIR)) {
		next if ($dir =~ /^\./ || length($dir) > 255 || $dir =~ /\.lock$/);
		my $path = $base.$dir;
		if (-d "$path") {
			last if do_for_each_ref($path);
			next;
		}
		if (read_ref("$git_dir/$path",\$sha1) < 0) {
			print STDERR "$path points nowhere!\n";
			next;
		}
=comment
		if (!has_sha1_file($sha1)) {
			print STDERR "$path does not point to a valid commit object!\n";
			next;
		}
=cut
		last if show_ref($path,$sha1);
	}
	closedir DIR;
	0;
}

sub write_head_info {
	do_for_each_ref("refs");
	show_ref("capabilities^{}", "0000000000000000000000000000000000000000") 
		unless $capabilities_sent;
}

@commands = ();

sub read_head_info {
	while (1) {
		my $len;
		my $ret = read(STDIN, $len, 4);
		die "packet_read error: $!\n" if !defined($ret) || $ret != 4;
		die "protocol error: bad line length character: <$len>" if $len !~ m/^[0-9a-fA-F]{4}$/;
		my $len = hex($len);
		last if !$len;
		$len -= 4;
		my $line;
		$ret = read(STDIN, $line, $len);
		die "packet_read error: $!\n" if !defined($ret) || $ret != $len;
		die "protocol error: bad line length $len" if $len != length($line);
		#print STDERR "packet_read: $line\n";
		die "protocol error: expected old/new/ref, got $line" 
			if $line !~ /^([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) ([^\0]+)(\0report-status)?$/;

		$report_status = 1 if defined($4);

		my $cmd = {
			'old_sha1' => $1,
			'new_sha1' => $2,
			'ref_name' => $3,
			'error_string' => 0,
		};
		$commands[$#commands+1] = $cmd;
	}
}

%PACK_TYPE = (
	OBJ_EXT => 0,
	OBJ_COMMIT => 1,
	OBJ_TREE => 2,
	OBJ_BLOB => 3,
	OBJ_TAG => 4,
	OBJ_DELTA => 7
);

$pack_buffer = undef;

sub fill {
	my ($min) = @_;

	if ($min > length($pack_buffer)) {
		my $buf;
		read(STDIN, $buf, 4096);
		$pack_buffer .= $buf;
	}
	substr($pack_buffer, 0, $min);
}

sub throw_away {
	my ($len) = @_;
	my $data = substr($pack_buffer, 0, $len);
	$pack_sha->add($data);
	print PACK $data;
	$pack_buffer = substr($pack_buffer, $len);
}

sub fill_and_throw {
	my ($len) = @_;
	$a = fill($len);
	throw_away($len);
	$a;
}

sub get_data {
	my ($size) = @_;

	my $x = inflateInit() or die "Cannot create a inflation stream\n";
	my $buffer = '';
	my ($output, $status, $c);
	while (1) {
		fill(1);
		$c = $pack_buffer;
		($output, $status) = $x->inflate(\$c);
		throw_away(length($pack_buffer)-length($c));
		$buffer .= $output if $status == Z_OK or $status == Z_STREAM_END;
		last if $status != Z_OK;
	}
	die "inflation failed\n" unless $status == Z_STREAM_END && length($buffer) == $size;
	$buffer;
}

sub unpack_non_delta_entry {
	my ($type, $size) = @_;

	my $buffer = get_data($size);
	#print STDERR "Got $type\n";
}

sub unpack_delta_entry {
	my ($size) = @_;

	my $base_sha1 = fill_and_throw(20);

	$buffer = get_data($size);
	#print STDERR "Got delta\n";
}

sub unpack_one {
	my ($nr, $total) = @_;
	my $c = fill_and_throw(1);
	$c = ord($c);
	my $type = ($c >> 4) & 7;
	my $size = ($c & 15);
	my $shift = 4;
	while ($c & 0x80) {
		$c = fill_and_throw(1);
		$c = ord($c);
		$size += ($c & 0x7f) << $shift;
		$shift += 7;
	}
	if (!defined($quiet)) {
		$last_sec = 0 if ! defined ($last_sec);
		$last_percent = 0 if ! defined ($last_percent);
		my $percentage = ($nr * 100) / $total;
		my $now = time();
		if ($percentage != $last_percent || $now != $last_sec) {
			$last_sec = $now;
			$last_percent = $percentage;
			printf STDERR "%4u%% (%u/%u) done\r", $percentage, $nr, $total;
		}
	}
	if ($type == $PACK_TYPE{OBJ_COMMIT} ||
		$type == $PACK_TYPE{OBJ_TREE} ||
		$type == $PACK_TYPE{OBJ_BLOB} ||
		$type == $PACK_TYPE{OBJ_TAG}) {
		unpack_non_delta_entry($type,$size);
	} elsif ($type == $PACK_TYPE{OBJ_DELTA}) {
		unpack_delta_entry($size);
	} else {
		die "bad object type $type\n";
	}
}

sub process_pack {
	$pack_sha = Digest::SHA1->new();
	my $signature = fill_and_throw(4);
	die "not a pack. Wrong signature" unless $signature =~ m/^PACK/;

	my $version = fill_and_throw(4);
	$version = unpack("N",$version);
	die "unknown pack file version $version" unless $version == 2 || $version == 3;

	my $nr_obj = fill_and_throw(4);
	$nr_obj = unpack("N",$nr_obj);

	for ($i = 0;$i < $nr_obj; $i ++) {
		unpack_one($i+1, $nr_obj);
	}

	my $digest_sha = $pack_sha->hexdigest();
	my $sha = unpack("H40",fill_and_throw(20));
	die "final sha did not match\n" unless $sha eq $digest_sha;
	$sha;
}

sub upload_pack {
	$basename = ".pack-".time();
	$pack_filename = "$git_dir/objects/pack/$basename.pack";
	$idx_filename = "$git_dir/objects/pack/$basename.idx";
	open PACK, ">$pack_filename"
		or die "Cannot create file $pack_filename\n";
	#open IDX, ">$idx_filename"
	#	or die "Cannot create file $idx_filename\n";
		
	$sha = process_pack();
	close PACK;
	#close IDX;
	$new_pack_filename = "$git_dir/objects/pack/pack-$sha.pack";
	#$new_idx_filename = "$git_dir/objects/pack/pack-$sha.idx";
	rename $pack_filename, $new_pack_filename
		or die "Cannot rename $pack_filename to $new_pack_filename\n";
	#rename $idx_filename, $new_idx_filename
	#	or die "Cannot rename $idx_filename to $new_idx_filename\n";
	0;
}

sub update {
	my ($cmd) = @_;
}

sub execute_commands {
	foreach $cmd (@commands) {
		update $cmd;
	}
	#run_update_post_hook
}

sub report {
	my ($status) = @_;

	packet_write (1, $status ? "unpack $status\n" : "unpack ok\n");
	foreach $cmd (@commands) {
		if (${$cmd}{'error_string'}) {
			packet_write (1, "ng ".${$cmd}{'ref_name'}." ".${$cmd}{'error_string'}."\n");
		} else {
			packet_write (1, "ok ".${$cmd}{'ref_name'}."\n");
		}
	}
	packet_flush (1);
}

$quiet = 1 unless -t STDERR;

foreach $arg (@ARGV) {
	usage() if $arg =~ m/^-/;
	usage() if defined($dir);
	$dir = $arg;
}

$| = 1;

usage() if !defined($dir);

setup_git_env;

enter_repo($dir, 0) or die "'$dir': unable to chdir or not a git archive\n";

write_head_info;

packet_flush(1);

read_head_info;

if ($#commands >= 0) {
	my $unpack_status = upload_pack;
	execute_commands if !$unpack_status;
	report($unpack_status) if $report_status;
}


Cập nhật 3 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | Git

Thứ bảy, 17 Tháng sáu năm 2006 23:39:40 ICT

Chương trình bí mật trong less

Gói less có một chương trình đi kèm là code2color. Chẳng có trang man nào cho code2color, tuy nhiên có thể xem hướng dẫn bằng

perldoc -F /usr/bin/code2color

Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ năm, 15 Tháng sáu năm 2006 21:46:04 ICT

Tách sftp ra khỏi openssh làm sftp transport cho git

Bước một hoàn tất sau khi trộn 4, 5 tập tin của openssh lại với nhau, lột bỏ một mớ hàm, đổi tên một mớ. Kết quả:

 Makefile            |    8
 buffer.c            |  452 +++++++++++++++++++++
 buffer.h            |   75 +++
 connect.c           |   11 -
 sftp-get.c          |   50 ++
 sftp-put.c          |   51 ++
 sftp-receive-pack.c |  402 ++++++++++++++++++
 sftp-receive-pack.h |    1
 sftp.c              | 1122 +++++++++++++++++++++++++++++++++++++++++++++++++++
 sftp.h              |   33 ++
 sftpdef.h           |   92 ++++
 11 files changed, 2294 insertions(+), 3 deletions(-)
 create mode 100644 buffer.c
 create mode 100644 buffer.h
 create mode 100644 sftp-get.c
 create mode 100644 sftp-put.c
 create mode 100644 sftp-receive-pack.c
 create mode 100644 sftp-receive-pack.h
 create mode 100644 sftp.c
 create mode 100644 sftp.h
 create mode 100644 sftpdef.h

Đã có thể dùng sftp-putsftp-get để truyền tập tin - hai phần quan trọng nhất để làm sftp transport. Phần còn lại là liệt kê danh sách thư mục, để làm for_each_ref. Cần ngâm cứu thêm một tí về giao thức được dùng bởi git-send-packgit-receive-pack vì không rõ làm thế nào để biết đâu là chỗ kết thúc của một pack nếu không phân tích nó. Và cũng chưa biết cách phát sinh SHA-1 cho pack.

Đời vẫn ổn khi tối về đi tu. Sáng mai lại vào tù.


Cập nhật 3 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | Git

Thứ sáu, 09 Tháng sáu năm 2006 00:41:09 ICT

aspell-vi

Vừa mới cho aspell-vi vào Portage. Tuy nhiên bắt lỗi theo kiểu aspell thì quá tệ. Chỉ bắt được những chữ sai thậm tệ. Cũng đúng thôi. vspell ơi vspell à..


Cập nhật 3 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | Tiếng Việt, Gentoo

Thứ năm, 08 Tháng sáu năm 2006 22:46:07 ICT

Các ứng dụng bằng dòng lệnh có tiếng Việt

Tình cờ thấy wget hiển thị vài chữ tiếng Việt. Gõ thử wget --help thì ra .. tiếng Việt! Mò luôn trong /usr/share/locale/vi/LC_MESSAGES thì thấy thêm bison, flex (má ơi), gawk, nano, psmisc, shadow (hic).

Mò lên Translation Project thì đã bị dịch sang tiếng Việt gần hết. Cám ơn những đóng góp của Clytie Siddall, Ngô Trần Thủy, Phan Vĩnh Thịnh, Trần Thị Hoàng Quyên và Trịnh Minh Thanh. Bây giờ cần người dùng và cho ý kiến!


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ năm, 08 Tháng sáu năm 2006 21:48:46 ICT

dia.git

Sau rhythmbox.git. Xin trân trọng giới thiệu Dia git repository (40MB). Tuy ít số commit hơn, nhưng có lẽ chứa nhiều hình hơn nên to hơn.

Bước kế tiếp, tống tất cả các thay đổi vào dia repo

Bước kế kế tiếp nữa, hic.. viết sftp transport cho git. Chán rsync lắm rồi


Cập nhật 3 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | Git

Thứ ba, 06 Tháng sáu năm 2006 22:18:03 ICT

Tiên sư thằng Gu Gồ

Sau Google Pages, Google Calendar, giờ đến Google Spreadsheets! Mà Google Spreadsheets lại không dùng XUL!!!

Bố khỉ mấy thằng điên ở tập đoàn điên Google. Chắc bỏ nghề quá :(


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ ba, 06 Tháng sáu năm 2006 18:28:42 ICT

Rhythmbox.git

Mất hai ngày để chuyển từ CVS sang GIT. Kết quả là Rhythmbox GIT repository (24MB)


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Chủ nhật, 04 Tháng sáu năm 2006 12:02:32 ICT

SoC năm nay có gì hay?

  • FFmpeg cài đặt VC-1 codec. Có cái này thì chơi WMV9 (WMV3) vô tư không cần dùng dll
  • Abiword có PDF Import plugin
  • Debian có Translation Coordination System
  • Gaim có Gaim cho text-mode Curses-based Gaim using libgaim
  • Gentoo chuyển CVS sang GIT (đúng ra đã làm xong, mất cỡ 30h và được 600MB).
  • Gentoo có thêm baselayout editor, X.org configuration tool
  • GNOME có đủ thứ, trong đó có libgnome-applet, Gstreamer Graphical Pipeline Editor, Long Running Task Manager & Nautilus/Epiphany Download Integration
  • MoinMoin DocBook XML Conversion tool (Fedora)
  • Xscreen

Cập nhật 3 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh | SoC

Thứ bảy, 03 Tháng sáu năm 2006 23:52:51 ICT

Rhythmbox CVS không hiện đúng tiếng Việt với gnome-media 2.6

Chết ngay sau hàm gnome_media_profiles_init. Không biết tại sao. Không quan tâm

Coi bộ nguyên tắc chia để trị, kiểm tra nội dung hàm _(), áp dụng cũng tạm được. Dù biên dịch 20 lần với một cái máy cùi cũng không vui lắm. Cũng coi như đáng một ngày.


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ bảy, 03 Tháng sáu năm 2006 18:58:09 ICT

Khi Nón Đỏ đi làm Quép

thì sự đời ra sao nhỉ?

http://mugshot.org/


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ sáu, 02 Tháng sáu năm 2006 23:34:59 ICT

Cẩn thận khi nâng cấp gcc

Nâng từ gcc 3.3.2 lên 3.3.6. Trong đống tập tin .la trong /usr/lib (những cái có dùng libstdc++) liên kết trực tiếp đến /usr/lib/gcc-lib/i486-pc-linux-gnu/3.3.2/. Hậu quả là những thư viện nào dùng libtool và dùng liên kết đến thư viện đó ngừng chạy một cách mượt mà với thông báo lỗi không tìm ra blah blah blah/3.3.2/blah/libstdc++.la.

Lệnh sau dùng để chỉnh từ 3.3.2 sang 3.3.6:

sed -i 's,/3\.3\.2,/3.3.6,g'  `grep 3.3.2 /usr/lib/*.la -l`

Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ sáu, 02 Tháng sáu năm 2006 22:54:48 ICT

Nếu mount không chịu chạy

Nó cứ ì ra đấy, im re, thì không hẳn là do nó bị lỗi. Dùng fsck để kiểm tra trước rồi mount lại sau.


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ năm, 01 Tháng sáu năm 2006 13:38:33 ICT

Tạo thumbnail trong nautilus cho flv

Lưu lại tập tin này (vd blah) rồi chạy lệnh sau:

gconftool-2 --install-schema-file=blah

Nội dung tập tin blah:

<gconfschemafile>
    <schemalist>
        <schema>
            <key>/schemas/desktop/gnome/thumbnailers/application@x-extension-flv/enable</key>
            <applyto>/desktop/gnome/thumbnailers/application@x-extension-flv/enable</applyto>
            <owner>totem</owner>
            <type>bool</type>
            <default>true</default>
            <locale name="C">
                <short></short>
                <long></long>
            </locale>
        </schema>
        <schema>
            <key>/schemas/desktop/gnome/thumbnailers/application@x-extension-flv/command</key>
            <applyto>/desktop/gnome/thumbnailers/application@x-extension-flv/command</applyto>
            <owner>totem</owner>
            <type>string</type>
            <default>/usr/bin/totem-video-thumbnailer -s %s %u %o</default>
            <locale name="C">
                <short></short>
                <long></long>
            </locale>
        </schema>
    </schemalist>
</gconfschemafile>

Khởi động lại nautilus (dường như nautilus không theo dõi những khoá này)


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh

Thứ năm, 01 Tháng sáu năm 2006 13:15:41 ICT

Dùng pitfdll để xem flv trong totem

Cài pitfdll bản CVS kèm với win32codes thì có thể dùng totem để xem các tập tin .flv. Cũng có thể nghe .rm bằng totem hoặc rhythmbox nhưng hình như rm plugin bị cảm nên nghe hơi nghẹt.

Vẫn có thể xem flv bằng gstreamer không cần dùng pitfdll, nhưng không hiểu sao totem không nhận ra. Sao nó ngu thế nhỉ?

PS. Hình như nâng cấp gstreamer lên thì totem xem mát trời không cần pitfdll. gstreamer-0.10.6, gst-plugins-base-0.10.7, gst-plugins-{good,bad,ugly}-0.10.3.


Cập nhật 2 lần. Lần cuối: Tue Aug 08 11:22:15+0011 2017

Tác giả: pclouds | Liên kết tĩnh