Happy Porting x86 Application to Android 2010-07 Owen Hsu
Outline <ul><li>Motivation </li></ul><ul><li>Solutions </li></ul><ul><li>Examples </li></ul>
Why Bother? <ul><li>Different instruction set  </li></ul><ul><ul><li>Most applications were programmed under x86 architect...
Solutions? <ul><li>The Traditional Make way </li></ul><ul><li>The NDK way </li></ul>
The Traditional Make Way <ul><li>Loop </li></ul><ul><ul><li>1. Configure </li></ul></ul><ul><ul><li>2. Modify the Makefile...
The NDK Way <ul><li>1. Configure </li></ul><ul><li>2. Write Android Makefile </li></ul><ul><li>3. Patch code </li></ul><ul...
Example <ul><li>Wget – Run as Android Executable </li></ul><ul><li>Curl - Run as Android Shared Library </li></ul>
Note <ul><li>These examples are made by NDK r3, the installation path is /usr/local/src/android-ndk-r3, it also marked as ...
Wget Porting by NDK  - 1. Configure <ul><li>1.1 Create NDK apps </li></ul><ul><ul><li># mkdir –p /PATH/TO/NDK/apps/wget/pr...
Wget Porting by NDK  - 2. Write Android Makefile <ul><li>2.1 Create NDK Application.mk </li></ul><ul><ul><li># vim /PATH/T...
Wget Porting by NDK - 3. Patch code <ul><li>3.1 Test run </li></ul><ul><ul><li># cd /PATH/TO/NDK </li></ul></ul><ul><ul><l...
Wget Porting by NDK - 3. Patch code <ul><li>3.3 Patch code </li></ul><ul><ul><li>Patch src/sysdep.h  </li></ul></ul><ul><u...
Wget Porting by NDK - 4. Run <ul><li>4.1 Start the emulator </li></ul><ul><ul><li># emulator –avd Donut </li></ul></ul><ul...
Wget Porting Reference <ul><li>http://jacob.hoffman-andrews.com/android/wget / </li></ul>
The Diff/Patch Tool HOWTO <ul><li>diff </li></ul><ul><ul><li>Compare the difference between two files </li></ul></ul><ul><...
Example of Patch Single File (1/4) <ul><li>1. Create test file </li></ul><ul><ul><li># cat >> test0 << EOF </li></ul></ul>...
Example of Patch Single File (2/4) <ul><li>2. Create patch file </li></ul><ul><ul><li># diff –uN test0 test1 > test1.patch...
Description of Patch file <ul><li>Description: </li></ul><ul><ul><li>Header: --- old file, +++ new file </li></ul></ul><ul...
Example of Patch Single File (3/4) <ul><li>3. Patch the target file </li></ul><ul><ul><li># patch –p0 < test1.patch </li><...
Example of Patch Single File (4/4) <ul><li>4. Reverse the patched file </li></ul><ul><ul><li># less test0 </li></ul></ul><...
Example of Patch Multiple Files <ul><li>1. Create test file </li></ul><ul><ul><li># mkdir prj0 </li></ul></ul><ul><ul><li>...
Example of Patch Multiple Files <ul><li>2. Create patch file </li></ul><ul><ul><li># diff –ruN prj0 prj1 > prj1.patch </li...
Example of Patch Multiple Files <ul><li>3. Patch the target file   </li></ul><ul><ul><li># cd prj0 </li></ul></ul><ul><ul>...
Example of Patch Multiple Files <ul><li>4. Reverse the patched file </li></ul><ul><ul><li># ls </li></ul></ul><ul><ul><li>...
Diff & Patch Reference <ul><li>http://www.xspace.idv.tw/bo_blog/read.php?97 </li></ul><ul><li>http://www.linuxforums.org/a...
Curl Porting by NDK  - 1. Configure <ul><li>1.1 Create NDK apps </li></ul><ul><ul><li># mkdir –p /PATH/TO/NDK/apps/curl/pr...
Curl Porting by NDK  - 1. Configure <ul><li>Notice: </li></ul><ul><ul><li>Config with &quot;--disable-nonblocking&quot; wi...
Curl Porting by NDK  - 2. Write Android Makefile <ul><li>Fortunately, a sample android.mk in source is included, we just n...
Curl Porting by NDK - 3. Patch code <ul><li>3.1 Patch code  </li></ul><ul><ul><li>Patch project/jni/lib/connect.c </li></u...
Curl Porting by NDK - 4. Run <ul><li>4.1 Start the emulator </li></ul><ul><ul><li># emulator –avd Donut </li></ul></ul><ul...
Curl Porting Reference <ul><li>http://curl.haxx.se/mail/lib-2009-12/0071.html </li></ul>
Upcoming SlideShare
Loading in...5
×

Happy porting x86 application to android

7,522
-1

Published on

This slide described how to port x86 application to arm-based android platform, and 2 examples are included

Published in: Technology
3 Comments
0 Likes
Statistics
Notes
  • Hello sanchess33,
    You can check your Application.mk and Android.mk, the APP_MODULES in Application.mk and LOCAL_MODULE in Android.mk should be the same, and the app folder name should be the same with your make command, like make APP={app folder name}.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • this is output

    root@debian:/android/ndk# make APP=wget V=1
    Android NDK: Building for application s 'wget'
    make: *** No rule to make target `wget', needed by `ndk-app-wget'. Stop.

    Thanks
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • congratulations for the article, very rich and useful.
    I'm having a problem to compile wget with ndk.
    I followed yours instructions but I get the following error

    #root@debian:/android/ndk# make APP=wget V1
    #Android NDK: Building for application s 'wget'
    #make: *** No rule to make target `V1'. Stop.

    Thanks in advance.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Be the first to like this

No Downloads
Views
Total Views
7,522
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
128
Comments
3
Likes
0
Embeds 0
No embeds

No notes for slide

Happy porting x86 application to android

  1. 1. Happy Porting x86 Application to Android 2010-07 Owen Hsu
  2. 2. Outline <ul><li>Motivation </li></ul><ul><li>Solutions </li></ul><ul><li>Examples </li></ul>
  3. 3. Why Bother? <ul><li>Different instruction set </li></ul><ul><ul><li>Most applications were programmed under x86 architecture, Android is run under ARM by default. </li></ul></ul><ul><li>Different variable types </li></ul><ul><ul><li>There is NO standard C library in Android, it provides Bionic C library instead. </li></ul></ul>
  4. 4. Solutions? <ul><li>The Traditional Make way </li></ul><ul><li>The NDK way </li></ul>
  5. 5. The Traditional Make Way <ul><li>Loop </li></ul><ul><ul><li>1. Configure </li></ul></ul><ul><ul><li>2. Modify the Makefile </li></ul></ul><ul><ul><li>3. Make </li></ul></ul><ul><ul><li>4. Patch </li></ul></ul><ul><li>… </li></ul>
  6. 6. The NDK Way <ul><li>1. Configure </li></ul><ul><li>2. Write Android Makefile </li></ul><ul><li>3. Patch code </li></ul><ul><li>4. Run </li></ul>
  7. 7. Example <ul><li>Wget – Run as Android Executable </li></ul><ul><li>Curl - Run as Android Shared Library </li></ul>
  8. 8. Note <ul><li>These examples are made by NDK r3, the installation path is /usr/local/src/android-ndk-r3, it also marked as /PATH/TO/NDK in later slide </li></ul><ul><li>The version of wget is 1.11.4, it is available in http://ftp.gnu.org/gnu/wget/wget-1.11.4.tar.gz , or in http://goo.gl/0inP </li></ul><ul><li>The version of Curl is 7.20.0, it is available in http://curl.haxx.se/download/curl-7.20.0.tar.bz2 , or in http://goo.gl/qGz6 </li></ul>
  9. 9. Wget Porting by NDK - 1. Configure <ul><li>1.1 Create NDK apps </li></ul><ul><ul><li># mkdir –p /PATH/TO/NDK/apps/wget/project/jni </li></ul></ul><ul><ul><li># cd /PATH/TO/NDK/apps/wget/project/jni </li></ul></ul><ul><li>1.2 Copy wget source to NDK apps </li></ul><ul><ul><li># cp –Rf /usr/local/src/wget-1.11.4/* . </li></ul></ul><ul><li>1.3 Disable most features, and create the Makefile </li></ul><ul><ul><li># ./configure --disable-opie --disable-digest --disable-ntlm --disable-debug --disable-largefile --disable-ipv6 --disable-nls --without-ssl </li></ul></ul><ul><li>Reference: Android_Makefile_Internal.pdf </li></ul>
  10. 10. Wget Porting by NDK - 2. Write Android Makefile <ul><li>2.1 Create NDK Application.mk </li></ul><ul><ul><li># vim /PATH/TO/NDK/apps/wget/Application.mk </li></ul></ul><ul><ul><li># begin code </li></ul></ul><ul><ul><li>APP_PROJECT_PATH := $(call my-dir)/project </li></ul></ul><ul><ul><li>APP_MODULES := wget </li></ul></ul><ul><ul><li># end code </li></ul></ul><ul><li>2.2 Create NDK Android.mk </li></ul><ul><ul><li># vim /PATH/TO/NDK/apps/project/jni/Android.mk </li></ul></ul><ul><ul><li># begin code </li></ul></ul><ul><ul><li>include $(call all-subdir-makefiles) </li></ul></ul><ul><ul><li># end code </li></ul></ul><ul><ul><li># vim /PATH/TO/NDK/apps/project/jni/src/Android.mk </li></ul></ul><ul><ul><li># begin code </li></ul></ul><ul><ul><li>LOCAL_PATH := $(call my-dir) </li></ul></ul><ul><ul><li>include $(CLEAR_VARS) </li></ul></ul><ul><ul><li>LOCAL_MODULE := wget </li></ul></ul><ul><ul><li>LOCAL_SRC_FILES := alloca.c cmpt.c connect.c convert.c cookies.c ftp-basic.c ftp-ls.c ftp-opie.c ftp.c getopt.c hash.c host.c html-parse.c html-url.c http.c init.c log.c main.c netrc.c progress.c ptimer.c recur.c res.c retr.c safe-ctype.c snprintf.c spider.c url.c utils.c version.c xmalloc.c </li></ul></ul><ul><ul><li>include $( BUILD_EXECUTABLE ) </li></ul></ul><ul><ul><li># end code </li></ul></ul>
  11. 11. Wget Porting by NDK - 3. Patch code <ul><li>3.1 Test run </li></ul><ul><ul><li># cd /PATH/TO/NDK </li></ul></ul><ul><ul><li># make APP=wget V=1 </li></ul></ul><ul><li>3.2 Check out the error message </li></ul><ul><ul><li>apps/wget/project/jni/src/sysdep.h:65: error: two or more data types in declaration specifiers </li></ul></ul><ul><ul><li>apps/wget/project/jni/src/sysdep.h:203:3: error: #error &quot;Cannot determine a 32-bit unsigned integer type&quot; </li></ul></ul><ul><ul><li>apps/wget/project/jni/src/sysdep.h:211: error: conflicting types for 'uintptr_t‘ </li></ul></ul><ul><ul><li>apps/wget/project/jni/src/sysdep.h:216: error: conflicting types for 'intptr_t' </li></ul></ul><ul><ul><li>apps/wget/project/jni/src/wget.h:162: error: redefinition of typedef 'off_t' </li></ul></ul><ul><ul><li>apps/wget/project/jni/src/options.h:119: error: expected specifier-qualifier-list before 'wgint' </li></ul></ul>
  12. 12. Wget Porting by NDK - 3. Patch code <ul><li>3.3 Patch code </li></ul><ul><ul><li>Patch src/sysdep.h </li></ul></ul><ul><ul><li>Patch src/utils.c </li></ul></ul><ul><ul><li>Patch src/wget.h </li></ul></ul><ul><ul><li>See: http://goo.gl/k2gt </li></ul></ul><ul><li>3.4 Make & Get executable file </li></ul><ul><ul><li># cd /PATH/TO/NDK </li></ul></ul><ul><ul><li># make APP=wget V=1 </li></ul></ul><ul><ul><li>Result </li></ul></ul><ul><ul><ul><li>Executable : wget </li></ul></ul></ul><ul><ul><ul><li>Install : wget => apps/wget/project/libs/armeabi </li></ul></ul></ul>
  13. 13. Wget Porting by NDK - 4. Run <ul><li>4.1 Start the emulator </li></ul><ul><ul><li># emulator –avd Donut </li></ul></ul><ul><li>4.2 Upload the wget binary under /PATH/TO/NDK/apps/wget/project/libs/armeabi </li></ul><ul><ul><li># adb push wget /system/bin </li></ul></ul><ul><li>4.3 Run the application </li></ul><ul><ul><li># adb shell chmod 755 /system/bin/wget </li></ul></ul><ul><ul><li># adb shell /system/bin/wget http://goo.gl/AsP5 </li></ul></ul>
  14. 14. Wget Porting Reference <ul><li>http://jacob.hoffman-andrews.com/android/wget / </li></ul>
  15. 15. The Diff/Patch Tool HOWTO <ul><li>diff </li></ul><ul><ul><li>Compare the difference between two files </li></ul></ul><ul><ul><li>Syntax: </li></ul></ul><ul><ul><ul><li>diff [OPTION] SOURCE DESTINATION </li></ul></ul></ul><ul><ul><ul><li>-r: recursive </li></ul></ul></ul><ul><ul><ul><li>-N: new file </li></ul></ul></ul><ul><ul><ul><li>-u: uniform format </li></ul></ul></ul><ul><li>patch </li></ul><ul><ul><li>Patch/restore file by patch-file </li></ul></ul><ul><ul><li>Syntax: </li></ul></ul><ul><ul><ul><li>patch [OPTION] < PATCH_FILE </li></ul></ul></ul><ul><ul><ul><li>-p0: from current directory </li></ul></ul></ul><ul><ul><ul><li>-p1: ignore the first level of directory </li></ul></ul></ul><ul><ul><ul><li>-R: reverse </li></ul></ul></ul><ul><ul><ul><li>-E: remove-empty-files </li></ul></ul></ul>
  16. 16. Example of Patch Single File (1/4) <ul><li>1. Create test file </li></ul><ul><ul><li># cat >> test0 << EOF </li></ul></ul><ul><ul><li>> 0000 </li></ul></ul><ul><ul><li>> 0000 </li></ul></ul><ul><ul><li>> EOF </li></ul></ul><ul><ul><li># cat >> test1 << EOF </li></ul></ul><ul><ul><li>> 1111 </li></ul></ul><ul><ul><li>> 1111 </li></ul></ul><ul><ul><li>> EOF </li></ul></ul>
  17. 17. Example of Patch Single File (2/4) <ul><li>2. Create patch file </li></ul><ul><ul><li># diff –uN test0 test1 > test1.patch </li></ul></ul><ul><ul><li># less test1.patch </li></ul></ul><ul><ul><li>--- test0 2009-06-04 18:19:07.000000000 +0800 </li></ul></ul><ul><ul><li>+++ test1 2009-06-04 18:19:13.000000000 +0800 </li></ul></ul><ul><ul><li>@@ -1,2 +1,2 @@ </li></ul></ul><ul><ul><li>-0000 </li></ul></ul><ul><ul><li>-0000 </li></ul></ul><ul><ul><li>+1111 </li></ul></ul><ul><ul><li>+1111 </li></ul></ul>
  18. 18. Description of Patch file <ul><li>Description: </li></ul><ul><ul><li>Header: --- old file, +++ new file </li></ul></ul><ul><ul><li>Hunk: @@ line number which is modified </li></ul></ul><ul><li> + new added </li></ul><ul><li> - new deleted </li></ul><ul><li>Example: </li></ul><ul><li>// Header </li></ul><ul><li>--- test0 2009-06-04 18:19:07.000000000 +0800 </li></ul><ul><li>+++ test1 2009-06-04 18:19:13.000000000 +0800 </li></ul><ul><li>// Hunk </li></ul><ul><li>@@ -1,2 +1,2 @@ </li></ul><ul><li>-0000 </li></ul><ul><li>-0000 </li></ul><ul><li>+1111 </li></ul><ul><li>+1111 </li></ul>
  19. 19. Example of Patch Single File (3/4) <ul><li>3. Patch the target file </li></ul><ul><ul><li># patch –p0 < test1.patch </li></ul></ul><ul><ul><li># less test0 </li></ul></ul><ul><ul><li>1111 </li></ul></ul><ul><ul><li>1111 </li></ul></ul>
  20. 20. Example of Patch Single File (4/4) <ul><li>4. Reverse the patched file </li></ul><ul><ul><li># less test0 </li></ul></ul><ul><ul><li>1111 </li></ul></ul><ul><ul><li>1111 </li></ul></ul><ul><ul><li># patch –RE –p0 < test1.patch </li></ul></ul><ul><ul><li># less test0 </li></ul></ul><ul><ul><li>0000 </li></ul></ul><ul><ul><li>0000 </li></ul></ul>
  21. 21. Example of Patch Multiple Files <ul><li>1. Create test file </li></ul><ul><ul><li># mkdir prj0 </li></ul></ul><ul><ul><li># cp test0 prj0 </li></ul></ul><ul><ul><li># cd prj0 </li></ul></ul><ul><ul><li># cat >> foo0 << EOF </li></ul></ul><ul><ul><li>> prj0/foo0 </li></ul></ul><ul><ul><li>> EOF </li></ul></ul><ul><ul><li># cd .. </li></ul></ul><ul><ul><li># mkdir prj1 </li></ul></ul><ul><ul><li># cp test1 prj1 </li></ul></ul><ul><ul><li># cd prj1 </li></ul></ul><ul><ul><li># cat >> foo1 << EOF </li></ul></ul><ul><ul><li>> prj1/foo1 </li></ul></ul><ul><ul><li>> EOF </li></ul></ul><ul><ul><li># cd .. </li></ul></ul>
  22. 22. Example of Patch Multiple Files <ul><li>2. Create patch file </li></ul><ul><ul><li># diff –ruN prj0 prj1 > prj1.patch </li></ul></ul><ul><ul><li># less prj1.patch </li></ul></ul><ul><ul><li>diff -ruN prj0/foo0 prj1/foo0 </li></ul></ul><ul><ul><li>--- prj0/foo0 2009-06-04 18:37:28.000000000 +0800 </li></ul></ul><ul><ul><li>+++ prj1/foo0 1970-01-01 08:00:00.000000000 +0800 </li></ul></ul><ul><ul><li>@@ -1 +0,0 @@ </li></ul></ul><ul><ul><li>-prj0/foo0 </li></ul></ul><ul><ul><li>diff -ruN prj0/foo1 prj1/foo1 </li></ul></ul><ul><ul><li>--- prj0/foo1 1970-01-01 08:00:00.000000000 +0800 </li></ul></ul><ul><ul><li>+++ prj1/foo1 2009-06-04 18:37:40.000000000 +0800 </li></ul></ul><ul><ul><li>@@ -0,0 +1 @@ </li></ul></ul><ul><ul><li>+prj1/foo1 </li></ul></ul><ul><ul><li>diff -ruN prj0/test0 prj1/test0 </li></ul></ul><ul><ul><li>--- prj0/test0 2009-06-04 18:31:55.000000000 +0800 </li></ul></ul><ul><ul><li>+++ prj1/test0 1970-01-01 08:00:00.000000000 +0800 </li></ul></ul><ul><ul><li>@@ -1,2 +0,0 @@ </li></ul></ul><ul><ul><li>-0000 </li></ul></ul><ul><ul><li>-0000 </li></ul></ul><ul><ul><li>diff -ruN prj0/test1 prj1/test1 </li></ul></ul><ul><ul><li>--- prj0/test1 1970-01-01 08:00:00.000000000 +0800 </li></ul></ul><ul><ul><li>+++ prj1/test1 2009-06-04 18:33:29.000000000 +0800 </li></ul></ul><ul><ul><li>@@ -0,0 +1,2 @@ </li></ul></ul><ul><ul><li>+1111 </li></ul></ul><ul><ul><li>+1111 </li></ul></ul>
  23. 23. Example of Patch Multiple Files <ul><li>3. Patch the target file </li></ul><ul><ul><li># cd prj0 </li></ul></ul><ul><ul><li># patch -p1 < ../prj1.patch </li></ul></ul><ul><ul><li># ls </li></ul></ul><ul><ul><li>foo1 test1 </li></ul></ul>
  24. 24. Example of Patch Multiple Files <ul><li>4. Reverse the patched file </li></ul><ul><ul><li># ls </li></ul></ul><ul><ul><li>foo1 test1 </li></ul></ul><ul><ul><li># patch –RE –p1 < ../prj1.patch </li></ul></ul><ul><ul><li># ls </li></ul></ul><ul><ul><li>foo0 test0 </li></ul></ul>
  25. 25. Diff & Patch Reference <ul><li>http://www.xspace.idv.tw/bo_blog/read.php?97 </li></ul><ul><li>http://www.linuxforums.org/articles/using-diff-and-patch_80.html </li></ul>
  26. 26. Curl Porting by NDK - 1. Configure <ul><li>1.1 Create NDK apps </li></ul><ul><ul><li># mkdir –p /PATH/TO/NDK/apps/curl/project/jni </li></ul></ul><ul><ul><li># cd /PATH/TO/NDK/apps/libcurl/project/jni </li></ul></ul><ul><li>1.2 Copy curl source to NDK apps </li></ul><ul><ul><li># cp –Rf /usr/local/src/curl-7.20.0/* . </li></ul></ul><ul><li>1.3 Disable most features, and create Makefile </li></ul><ul><ul><li># ./configure --disable-debug --disable-optimize --disable-warnings --disable-curldebug --disable-ares --disable-dependency-tracking --disable-largefile --disable-libtool-lock --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smtp --disable-manual --disable-ipv6 --disable-verbose --disable-sspi --disable-crypto-auth --disable-cookies --disable-hidden-symbols --disable-soname-bump --without-ssl --without-zlib --without-gnutls --without-nss --without-ca-path --without-libssh2 --without-libidn </li></ul></ul>
  27. 27. Curl Porting by NDK - 1. Configure <ul><li>Notice: </li></ul><ul><ul><li>Config with &quot;--disable-nonblocking&quot; will cause the abnormal data transfer </li></ul></ul>
  28. 28. Curl Porting by NDK - 2. Write Android Makefile <ul><li>Fortunately, a sample android.mk in source is included, we just need to do some modifications </li></ul><ul><li>Two-libs format: </li></ul><ul><ul><li>Compile first library as shared library </li></ul></ul><ul><ul><li>Compile second library as executable file </li></ul></ul>
  29. 29. Curl Porting by NDK - 3. Patch code <ul><li>3.1 Patch code </li></ul><ul><ul><li>Patch project/jni/lib/connect.c </li></ul></ul><ul><ul><li>Patch project/jni/lib/url.c </li></ul></ul><ul><ul><li>See: http://goo.gl/LHbR </li></ul></ul><ul><li>3.2 Add C program to invoke Curl library </li></ul><ul><li>3.3 Make & Get shared library </li></ul><ul><ul><li># cd /PATH/TO/NDK </li></ul></ul><ul><ul><li># make APP=c-call-curl V=1 </li></ul></ul><ul><ul><li>Result: </li></ul></ul><ul><ul><li>SharedLibrary : libcurl.so </li></ul></ul><ul><ul><li>Install : libcurl.so => apps/c-call-curl/project/libs/armeabi </li></ul></ul><ul><ul><li>Compile thumb : c-call-curl <= apps/c-call-curl/project/jni/simple.c </li></ul></ul><ul><ul><li>Executable : c-call-curl </li></ul></ul><ul><ul><li>Install : c-call-curl => apps/c-call-curl/project/libs/armeabi </li></ul></ul>
  30. 30. Curl Porting by NDK - 4. Run <ul><li>4.1 Start the emulator </li></ul><ul><ul><li># emulator –avd Donut </li></ul></ul><ul><li>4.2 Upload the Curl library & C program under /PATH/TO/NDK/apps/curl/project/libs/armeabi </li></ul><ul><ul><li># adb remount </li></ul></ul><ul><ul><li># adb push c-call-curl /system/bin </li></ul></ul><ul><ul><li># adb push libcurl.so /system/lib </li></ul></ul><ul><li>4.3 Run the application </li></ul><ul><ul><li># adb shell chmod 755 /system/bin/c-call-curl </li></ul></ul><ul><ul><li># adb shell /system/bin/c-call-curl </li></ul></ul>
  31. 31. Curl Porting Reference <ul><li>http://curl.haxx.se/mail/lib-2009-12/0071.html </li></ul>
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×