First cut. Distintly raw around the edges:
* Assumes it will be running in /home/ali/wk/slashem/web.scripts
* Assumes cache directory will be in topdir
* No build system (simple compiling and linking against libxml2)
* No configure system (eg., tagsoup)
* Output XML untested
* Doesn't set bugzilla maintainer or exporter
* Handling of artifact priorities and resolution is suspect
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/README.cache Thu Nov 16 08:30:26 2006 +0000
1.3 @@ -0,0 +1,19 @@
1.4 +The cache directory contains the following:
1.5 +
1.6 +sf/attachments/<file_id>
1.7 + Raw attachments as downloaded from sourceforge by sf2bz
1.8 +
1.9 +sf/artifacts/<atid>/<aid>.html
1.10 + Tagsoup detailed artifacts as downloaded from sourceforge by sf2bz
1.11 +
1.12 +sf/users/<user_id>.html
1.13 + Tagsoup user profiles as downloaded from sourceforge by sf2bz
1.14 +
1.15 +attachments/<file_id>.xml
1.16 + Attachments converted to xml by sf2bz
1.17 +
1.18 +artifacts/<atid>/<aid>.xhtml
1.19 + Conversion of detailed artifacts to xhtml by tagsoup
1.20 +
1.21 +users/<user_id>.xhtml
1.22 + Conversion of user profiles to xhtml by tagsoup
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/attachment_descs.xsl Thu Nov 16 08:30:26 2006 +0000
2.3 @@ -0,0 +1,51 @@
2.4 +<?xml version="1.0"?>
2.5 +
2.6 +<!-- This style sheet takes a sourceforge artifact in html and outputs
2.7 + the attachment descriptions that it finds. The output is as follows:
2.8 + <attachments>
2.9 + <attachment>
2.10 + <attachid>###</attachid>
2.11 + <desc>...</desc>
2.12 + </attachment>
2.13 + ...
2.14 + </attachments>
2.15 +-->
2.16 +
2.17 +<xsl:stylesheet version="1.0"
2.18 + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
2.19 + xmlns:xhtml="http://www.w3.org/1999/xhtml"
2.20 + exclude-result-prefixes="xhtml">
2.21 +
2.22 +<xsl:template match="xhtml:tr" mode="download">
2.23 + <attachment>
2.24 + <attachid>
2.25 + <xsl:value-of select="substring-before(substring-after(xhtml:td[4]/descendant::xhtml:a/@href,'file_id='),'&')"/>
2.26 + </attachid>
2.27 + <desc>
2.28 + <xsl:value-of select="normalize-space(xhtml:td[3])"/>
2.29 + </desc>
2.30 + </attachment>
2.31 +</xsl:template>
2.32 +
2.33 +<xsl:template match="xhtml:table[preceding-sibling::xhtml:h4='Attached Files:' and xhtml:tr/xhtml:td='Download']">
2.34 + <xsl:for-each select="xhtml:tr[descendant::xhtml:a='Download']">
2.35 + <xsl:apply-templates select="." mode="download"/>
2.36 + </xsl:for-each>
2.37 +</xsl:template>
2.38 +
2.39 +<xsl:template match="/">
2.40 + <attachments>
2.41 + <xsl:apply-templates/>
2.42 + <!-- A null description used as a fallback for when there is no
2.43 + description listed for an attachment. -->
2.44 + <desc xml:id="null"/>
2.45 + </attachments>
2.46 +</xsl:template>
2.47 +
2.48 +<xsl:template match="node()" priority="-1">
2.49 + <xsl:apply-templates/>
2.50 +</xsl:template>
2.51 +
2.52 +<xsl:template match="text()" priority="-1"></xsl:template>
2.53 +
2.54 +</xsl:stylesheet>
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/attachment_details.xsl Thu Nov 16 08:30:26 2006 +0000
3.3 @@ -0,0 +1,70 @@
3.4 +<?xml version="1.0"?>
3.5 +
3.6 +<!-- This stylesheet extracts some details of attachments from the artifact
3.7 + history of a sourceforge project export. -->
3.8 +
3.9 +<xsl:stylesheet version="1.0"
3.10 + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
3.11 + xmlns:xi="http://www.w3.org/2001/XInclude">
3.12 +
3.13 +<xsl:import href="sf2bz.xsl"/>
3.14 +
3.15 +<!-- Process a sourceforge artifact, outputting attachments as found -->
3.16 +<xsl:template name="process-artifact">
3.17 + <xsl:apply-templates/>
3.18 + <xsl:for-each
3.19 + select="field[@name='artifact_history']/history[field[@name='field_name']='File Added']">
3.20 + <xsl:variable name="file_id"
3.21 + select="substring-before(field[@name='old_value'],':')"/>
3.22 + <attachment>
3.23 + <!-- isobsolete is an attribute of bugzilla:attachment, but that
3.24 + proves hard to deal with so we treat it as a node and fix it
3.25 + up in attachment_post.xsl -->
3.26 + <isobsolete>
3.27 + <xsl:value-of select="number(boolean(../history[field[@name='field_name']='File Deleted' and substring-before(field[@name='old_value'],':')=$file_id]))"/>
3.28 + </isobsolete>
3.29 + <attachid>
3.30 + <xsl:value-of select="$file_id"/>
3.31 + </attachid>
3.32 + <date>
3.33 + <xsl:call-template name="bugzilla-timestamp">
3.34 + <xsl:with-param name="secs" select="field[@name='entrydate']"/>
3.35 + </xsl:call-template>
3.36 + </date>
3.37 + <filename>
3.38 + <xsl:value-of
3.39 + select="substring-after(field[@name='old_value'],': ')"/>
3.40 + </filename>
3.41 + </attachment>
3.42 + </xsl:for-each>
3.43 +</xsl:template>
3.44 +
3.45 +<xsl:template match="project_export">
3.46 + <attachments>
3.47 + <xsl:apply-templates/>
3.48 + </attachments>
3.49 +</xsl:template>
3.50 +
3.51 +<xsl:template match="artifacts">
3.52 + <xsl:apply-templates/>
3.53 +</xsl:template>
3.54 +
3.55 +<xsl:template match="artifact">
3.56 + <xsl:variable name="type"
3.57 + select="normalize-space(field[@name='artifact_type'])"/>
3.58 + <xsl:choose>
3.59 + <xsl:when test="$type = 'Feature Requests'">
3.60 + <xsl:call-template name="process-artifact"/>
3.61 + </xsl:when>
3.62 + <xsl:when test="$type = 'Patches'">
3.63 + <xsl:call-template name="process-artifact"/>
3.64 + </xsl:when>
3.65 + <xsl:when test="$type = 'Bugs'">
3.66 + <xsl:call-template name="process-artifact"/>
3.67 + </xsl:when>
3.68 + </xsl:choose>
3.69 +</xsl:template>
3.70 +
3.71 +<xsl:template match="node()|text()" priority="-1"></xsl:template>
3.72 +
3.73 +</xsl:stylesheet>
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/attachment_gather.xsl Thu Nov 16 08:30:26 2006 +0000
4.3 @@ -0,0 +1,54 @@
4.4 +<?xml version="1.0"?>
4.5 +
4.6 +<!-- This style sheet takes the output of xml_attached file (which represents
4.7 + an attachment and it's MIME type), adds in the following extra nodes:
4.8 + date, desc & filename.
4.9 +-->
4.10 +
4.11 +<xsl:stylesheet version="1.0"
4.12 + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4.13 + xmlns:xi="http://www.w3.org/2001/XInclude">
4.14 +
4.15 +<xsl:template match="attachment">
4.16 + <xsl:copy>
4.17 + <xsl:for-each select="@*">
4.18 + <xsl:copy/>
4.19 + </xsl:for-each>
4.20 + <xsl:apply-templates/>
4.21 + <xsl:element namespace="http://www.w3.org/2001/XInclude" name="include">
4.22 + <xsl:attribute name="href">
4.23 + <xsl:text>attachment-details.xml</xsl:text>
4.24 + </xsl:attribute>
4.25 + <xsl:attribute name="xpointer">
4.26 + <xsl:text>xpointer(/attachments/attachment[attachid='</xsl:text>
4.27 + <xsl:value-of select="attachid"/>
4.28 + <xsl:text>']/*)</xsl:text>
4.29 + </xsl:attribute>
4.30 + </xsl:element>
4.31 + <xsl:element namespace="http://www.w3.org/2001/XInclude" name="include"> <xsl:attribute name="href">
4.32 + <xsl:text>attachment-descriptions.xml</xsl:text>
4.33 + </xsl:attribute>
4.34 + <xsl:attribute name="xpointer">
4.35 + <xsl:text>xpointer(/attachments/attachment[attachid='</xsl:text>
4.36 + <xsl:value-of select="attachid"/>
4.37 + <xsl:text>']/desc)element(null)</xsl:text>
4.38 + </xsl:attribute>
4.39 + </xsl:element>
4.40 + </xsl:copy>
4.41 +</xsl:template>
4.42 +
4.43 +<!-- Drop the attachment ID to avoid duplication with the node included
4.44 + from attachment-details.xml -->
4.45 +
4.46 +<xsl:template match="attachid"/>
4.47 +
4.48 +<xsl:template match="*">
4.49 + <xsl:copy>
4.50 + <xsl:for-each select="@*">
4.51 + <xsl:copy/>
4.52 + </xsl:for-each>
4.53 + <xsl:apply-templates/>
4.54 + </xsl:copy>
4.55 +</xsl:template>
4.56 +
4.57 +</xsl:stylesheet>
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/attachment_post.xsl Thu Nov 16 08:30:26 2006 +0000
5.3 @@ -0,0 +1,38 @@
5.4 +<?xml version="1.0"?>
5.5 +
5.6 +<!-- This stylesheet does some post-processing on attachments. -->
5.7 +
5.8 +<xsl:stylesheet version="1.0"
5.9 + xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
5.10 +
5.11 +<xsl:template match="attachment">
5.12 + <!-- Avoid xsl:copy so that any attributes (including xml:base) are lost -->
5.13 + <attachment>
5.14 + <xsl:attribute name="ispatch">
5.15 + <xsl:value-of select="@ispatch"/>
5.16 + </xsl:attribute>
5.17 + <xsl:attribute name="isobsolete">
5.18 + <xsl:value-of select="isobsolete"/>
5.19 + </xsl:attribute>
5.20 + <xsl:apply-templates/>
5.21 + </attachment>
5.22 +</xsl:template>
5.23 +
5.24 +<xsl:template match="attachment/isobsolete"/>
5.25 +
5.26 +<xsl:template match="attachment/desc[normalize-space(.)='']">
5.27 + <desc>
5.28 + <xsl:value-of select="../filename"/>
5.29 + </desc>
5.30 +</xsl:template>
5.31 +
5.32 +<xsl:template match="*">
5.33 + <xsl:copy>
5.34 + <xsl:for-each select="@*">
5.35 + <xsl:copy/>
5.36 + </xsl:for-each>
5.37 + <xsl:apply-templates/>
5.38 + </xsl:copy>
5.39 +</xsl:template>
5.40 +
5.41 +</xsl:stylesheet>
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/bugzilla.xsl Thu Nov 16 08:30:26 2006 +0000
6.3 @@ -0,0 +1,236 @@
6.4 +<?xml version="1.0"?>
6.5 +<xsl:stylesheet version="1.0"
6.6 + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
6.7 + xmlns:xi="http://www.w3.org/2001/XInclude"
6.8 + xmlns:date="http://exslt.org/dates-and-times"
6.9 + xmlns:sf2bz="http://project.juiblex.co.uk/sf2bz"
6.10 + extension-element-prefixes="date">
6.11 +
6.12 +<xsl:import href="sf2bz.xsl"/>
6.13 +
6.14 +<!-- Return the last change made in an artifact's history -->
6.15 +<xsl:template name="lastchange">
6.16 + <xsl:variable name="dates">
6.17 + <xsl:for-each
6.18 + select="field[@name='artifact_history']/history/field[@name='entrydate']">
6.19 + <xsl:sort data-type='number' order='descending'/>
6.20 + <xsl:value-of select="."/>
6.21 + <xsl:text>:</xsl:text>
6.22 + </xsl:for-each>
6.23 + </xsl:variable>
6.24 + <xsl:call-template name="bugzilla-timestamp">
6.25 + <xsl:with-param name="secs" select="substring-before($dates,':')"/>
6.26 + </xsl:call-template>
6.27 +</xsl:template>
6.28 +
6.29 +<!-- Process a sourceforge artifact, outputting a bugzilla bug -->
6.30 +<xsl:template name="process-artifact">
6.31 + <bug>
6.32 + <xsl:apply-templates/>
6.33 + <delta_ts>
6.34 + <xsl:choose>
6.35 + <xsl:when test="field[@name='artifact_history']">
6.36 + <xsl:call-template name="lastchange"/>
6.37 + </xsl:when>
6.38 + <xsl:otherwise>
6.39 + <xsl:call-template name="bugzilla-timestamp">
6.40 + <xsl:with-param name="secs" select="field[@name='open_date']"/>
6.41 + </xsl:call-template>
6.42 + </xsl:otherwise>
6.43 + </xsl:choose>
6.44 + </delta_ts>
6.45 + <xsl:for-each
6.46 + select="field[@name='artifact_history']/history[field[@name='field_name']='File Added']">
6.47 + <xsl:element namespace="http://www.w3.org/2001/XInclude" name="include">
6.48 + <xsl:attribute name="href">
6.49 + <xsl:text>cache/attachments/</xsl:text>
6.50 + <xsl:value-of
6.51 + select="substring-before(field[@name='old_value'],':')"/>
6.52 + <xsl:text>.xml</xsl:text>
6.53 + </xsl:attribute>
6.54 + <xsl:attribute name="xpointer">xpointer(/attachment)</xsl:attribute>
6.55 + <xsl:element namespace="http://www.w3.org/2001/XInclude"
6.56 + name="fallback"/>
6.57 + </xsl:element>
6.58 + </xsl:for-each>
6.59 + </bug>
6.60 +</xsl:template>
6.61 +
6.62 +<!-- Output a user node -->
6.63 +<xsl:template name="bugzilla-user">
6.64 + <xsl:param name="element">who</xsl:param>
6.65 + <xsl:param name="user" select="."/>
6.66 + <sf2bz:element>
6.67 + <xsl:attribute name="name">
6.68 + <xsl:value-of select="$element"/>
6.69 + </xsl:attribute>
6.70 + <sf2bz:copy-from>
6.71 + <xsl:element namespace="http://www.w3.org/2001/XInclude" name="include">
6.72 + <xsl:attribute name="href">users.xml</xsl:attribute>
6.73 + <xsl:attribute name="xpointer">
6.74 + <xsl:text>xpointer(/users/user[.="</xsl:text>
6.75 + <xsl:value-of select="$user"/>
6.76 + <xsl:text>@users.sourceforge.net"])</xsl:text>
6.77 + </xsl:attribute>
6.78 + </xsl:element>
6.79 + </sf2bz:copy-from>
6.80 + </sf2bz:element>
6.81 +</xsl:template>
6.82 +
6.83 +<xsl:template match="project_export">
6.84 + <bugzilla version="2.23.3">
6.85 + <xsl:if test="news/field[@name='group_id']/@group_id">
6.86 + <xsl:attribute name="urlbase">
6.87 + <xsl:text>https://sourceforge.net/tracker/?group_id=</xsl:text>
6.88 + <xsl:value-of select="news/field[@name='group_id']/@group_id"/>
6.89 + </xsl:attribute>
6.90 + </xsl:if>
6.91 + <xsl:apply-templates/>
6.92 + </bugzilla>
6.93 +</xsl:template>
6.94 +
6.95 +<xsl:template match="artifacts">
6.96 + <xsl:apply-templates/>
6.97 +</xsl:template>
6.98 +
6.99 +<xsl:template match="artifact">
6.100 + <xsl:variable name="type"
6.101 + select="normalize-space(field[@name='artifact_type'])"/>
6.102 + <xsl:choose>
6.103 + <xsl:when test="$type = 'Feature Requests'">
6.104 + <xsl:call-template name="process-artifact"/>
6.105 + </xsl:when>
6.106 + <xsl:when test="$type = 'Patches'">
6.107 + <xsl:call-template name="process-artifact"/>
6.108 + </xsl:when>
6.109 + <xsl:when test="$type = 'Bugs'">
6.110 + <xsl:call-template name="process-artifact"/>
6.111 + </xsl:when>
6.112 + </xsl:choose>
6.113 +</xsl:template>
6.114 +
6.115 +<xsl:template match="field[@name='artifact_id']">
6.116 + <bug_id><xsl:value-of select="."/></bug_id>
6.117 +</xsl:template>
6.118 +
6.119 +<xsl:template match="field[@name='submitted_by']">
6.120 + <xsl:call-template name="bugzilla-user">
6.121 + <xsl:with-param name="element">reporter</xsl:with-param>
6.122 + </xsl:call-template>
6.123 +</xsl:template>
6.124 +
6.125 +<xsl:template match="field[@name='assigned_to']">
6.126 + <xsl:if test="normalize-space(.)!='nobody'">
6.127 + <xsl:call-template name="bugzilla-user">
6.128 + <xsl:with-param name="element">assigned_to</xsl:with-param>
6.129 + </xsl:call-template>
6.130 + </xsl:if>
6.131 +</xsl:template>
6.132 +
6.133 +<xsl:template match="field[@name='priority']">
6.134 + <priority>
6.135 + <xsl:choose>
6.136 + <xsl:when test="number(.)>8">P5</xsl:when>
6.137 + <xsl:when test="number(.)>5">P4</xsl:when>
6.138 + <xsl:when test="number(.)=5">P3</xsl:when>
6.139 + <xsl:when test="number(.)>1">P2</xsl:when>
6.140 + <xsl:otherwise>P1</xsl:otherwise>
6.141 + </xsl:choose>
6.142 + </priority>
6.143 +</xsl:template>
6.144 +
6.145 +<xsl:template match="field[@name='status']">
6.146 + <bug_status>
6.147 + <xsl:choose>
6.148 + <xsl:when test="normalize-space(.)='Open'">
6.149 + <xsl:choose>
6.150 + <xsl:when
6.151 + test="normalize-space(../field[@name='assigned_to'])='nobody'">
6.152 + <xsl:text>UNCONFIRMED</xsl:text>
6.153 + </xsl:when>
6.154 + <xsl:otherwise>ASSIGNED</xsl:otherwise>
6.155 + </xsl:choose>
6.156 + </xsl:when>
6.157 + <xsl:when test="normalize-space(.)='Pending'">NEW</xsl:when>
6.158 + <xsl:otherwise>RESOLVED</xsl:otherwise>
6.159 + </xsl:choose>
6.160 + </bug_status>
6.161 +</xsl:template>
6.162 +
6.163 +<xsl:template match="field[@name='summary']">
6.164 + <short_desc><xsl:value-of select="."/></short_desc>
6.165 +</xsl:template>
6.166 +
6.167 +<xsl:template match="field[@name='open_date']">
6.168 + <creation_ts>
6.169 + <xsl:call-template name="bugzilla-timestamp"/>
6.170 + </creation_ts>
6.171 +</xsl:template>
6.172 +
6.173 +<xsl:template match="field[@name='artifact_type']">
6.174 + <xsl:variable name="pri"
6.175 + select="normalize-space(../field[@name='priority'])"/>
6.176 + <bug_severity>
6.177 + <xsl:choose>
6.178 + <xsl:when test="normalize-space(.)='Bugs'">
6.179 + <xsl:choose>
6.180 + <xsl:when test="$pri>=9">blocker</xsl:when>
6.181 + <xsl:when test="$pri=8">critical</xsl:when>
6.182 + <xsl:when test="$pri<8 and $pri>5">major</xsl:when>
6.183 + <xsl:when test="$pri<5 and $pri>2">minor</xsl:when>
6.184 + <xsl:when test="$pri<=2">trivial</xsl:when>
6.185 + <xsl:otherwise>normal</xsl:otherwise>
6.186 + </xsl:choose>
6.187 + </xsl:when>
6.188 + <xsl:otherwise>enhancement</xsl:otherwise>
6.189 + </xsl:choose>
6.190 + </bug_severity>
6.191 +</xsl:template>
6.192 +
6.193 +<xsl:template match="field[@name='category']">
6.194 + <component><xsl:value-of select="."/></component>
6.195 +</xsl:template>
6.196 +
6.197 +<xsl:template match="field[@name='artifact_group_id']">
6.198 + <version><xsl:value-of select="."/></version>
6.199 +</xsl:template>
6.200 +
6.201 +<xsl:template match="field[@name='details']">
6.202 + <long-desc>
6.203 + <xsl:call-template name="bugzilla-user">
6.204 + <xsl:with-param name="user" select="../field[@name='submitted_by']"/>
6.205 + </xsl:call-template>
6.206 + <bug_when>
6.207 + <xsl:call-template name="bugzilla-timestamp">
6.208 + <xsl:with-param name="secs" select="../field[@name='open_date']"/>
6.209 + </xsl:call-template>
6.210 + </bug_when>
6.211 + <thetext><xsl:value-of select="."/></thetext>
6.212 + </long-desc>
6.213 +</xsl:template>
6.214 +
6.215 +<xsl:template match="field[@name='artifact_messages']">
6.216 + <xsl:apply-templates/>
6.217 +</xsl:template>
6.218 +
6.219 +<xsl:template match="message">
6.220 + <long-desc>
6.221 + <xsl:call-template name="bugzilla-user">
6.222 + <xsl:with-param name="user" select="field[@name='user_name']"/>
6.223 + </xsl:call-template>
6.224 + <bug_when>
6.225 + <xsl:call-template name="bugzilla-timestamp">
6.226 + <xsl:with-param name="secs" select="field[@name='adddate']"/>
6.227 + </xsl:call-template>
6.228 + </bug_when>
6.229 + <thetext>
6.230 + <!-- Sourceforge puts either "Logged In: YES \n;user_id=%user\n\n" or
6.231 + "Logged In: NO\n\n" at the start of each message body. -->
6.232 + <xsl:value-of select="substring-after(field[@name='body'],' ')"/>
6.233 + </thetext>
6.234 + </long-desc>
6.235 +</xsl:template>
6.236 +
6.237 +<xsl:template match="node()|text()" priority="-1"></xsl:template>
6.238 +
6.239 +</xsl:stylesheet>
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/bugzilla_post.xsl Thu Nov 16 08:30:26 2006 +0000
7.3 @@ -0,0 +1,31 @@
7.4 +<?xml version="1.0"?>
7.5 +
7.6 +<!-- This stylesheet does some post-processing on the bugzilla output. -->
7.7 +
7.8 +<xsl:stylesheet version="1.0"
7.9 + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
7.10 + xmlns:sf2bz="http://project.juiblex.co.uk/sf2bz"
7.11 + exclude-result-prefixes="sf2bz">
7.12 +
7.13 +<xsl:template match="//sf2bz:element">
7.14 + <xsl:element name="{@name}">
7.15 + <xsl:for-each select="sf2bz:copy-from/*/*|sf2bz:copy-from/*/@*|sf2bz:copy-from/*/text()">
7.16 + <xsl:copy/>
7.17 + </xsl:for-each>
7.18 + </xsl:element>
7.19 +</xsl:template>
7.20 +
7.21 +<!-- Kill all the xmlns:* and xml:base attributes
7.22 + (none of which are relevant any more) -->
7.23 +<xsl:template match="*">
7.24 + <xsl:element name="{name()}">
7.25 + <xsl:for-each select="@*">
7.26 + <xsl:if test="name(.) != 'xml:base'">
7.27 + <xsl:copy/>
7.28 + </xsl:if>
7.29 + </xsl:for-each>
7.30 + <xsl:apply-templates/>
7.31 + </xsl:element>
7.32 +</xsl:template>
7.33 +
7.34 +</xsl:stylesheet>
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/get_attached_files.c Thu Nov 16 08:30:26 2006 +0000
8.3 @@ -0,0 +1,148 @@
8.4 +/*
8.5 + * This program parses the XML data provided by sourceforge to extract the
8.6 + * list of "attached files" (which are not themselves included in the XML
8.7 + * stream and therefore need to be seperately downloaded for archiving).
8.8 + */
8.9 +
8.10 +#include <stdlib.h>
8.11 +#include <stdio.h>
8.12 +#include <string.h>
8.13 +#include <libxml/xmlmemory.h>
8.14 +#include <libxml/parser.h>
8.15 +
8.16 +struct artifact_type {
8.17 + char *name;
8.18 + int id;
8.19 +} artifact_types[] = {
8.20 + "Bugs", 109746,
8.21 + "Support Requests", 209746,
8.22 + "Patches", 309746,
8.23 + "Feature Requests", 359746,
8.24 +};
8.25 +
8.26 +#ifndef NO_ELEMS
8.27 +#define NO_ELEMS(array) (sizeof(array)/sizeof(*(array)))
8.28 +#endif
8.29 +
8.30 +void parse_history(xmlDocPtr doc,const xmlChar *artifact_id,
8.31 + const xmlChar *artifact_type,xmlNodePtr node)
8.32 +{
8.33 + xmlNodePtr field,cur;
8.34 + xmlAttrPtr attr;
8.35 + xmlChar *name=NULL;
8.36 + xmlChar *field_name=NULL;
8.37 + xmlChar *text;
8.38 + int file_id,i;
8.39 + for(field=node->xmlChildrenNode;field;field=field->next)
8.40 + if (!xmlStrcmp(field->name,(const xmlChar *)"field"))
8.41 + {
8.42 + for(attr=field->properties;attr;attr=attr->next)
8.43 + if (!xmlStrcmp(attr->name,(const xmlChar *)"name"))
8.44 + {
8.45 + name=xmlNodeListGetString(doc,attr->children,1);
8.46 + if (!xmlStrcmp(name,(const xmlChar *)"field_name"))
8.47 + field_name=xmlNodeListGetString(doc,field->children,1);
8.48 + if (!xmlStrcmp(field_name,(const xmlChar *)"File Added") &&
8.49 + !xmlStrcmp(name,(const xmlChar *)"old_value"))
8.50 + {
8.51 + text=xmlNodeListGetString(doc,field->children,1);
8.52 + if (sscanf(text,"%d",&file_id)==1)
8.53 + {
8.54 + for(i=NO_ELEMS(artifact_types)-1;i>=0;i--)
8.55 + if (!strcmp(artifact_types[i].name,
8.56 + (const char *)artifact_type))
8.57 + break;
8.58 + if (i>=0)
8.59 + printf("%d %s %d\n",
8.60 + artifact_types[i].id,artifact_id,file_id);
8.61 + else
8.62 + fprintf(stderr,
8.63 + "Warning: Unknown artifact type \"%s\" - "
8.64 + "attached file %s ignored\n",
8.65 + artifact_type,text);
8.66 + }
8.67 + xmlFree(text);
8.68 + }
8.69 + xmlFree(name);
8.70 + }
8.71 + }
8.72 + if (field_name)
8.73 + xmlFree(field_name);
8.74 +}
8.75 +
8.76 +void parse_artifact(xmlDocPtr doc,xmlNodePtr node)
8.77 +{
8.78 + xmlNodePtr field,cur;
8.79 + xmlAttrPtr attr;
8.80 + xmlChar *name=NULL;
8.81 + xmlChar *id=NULL;
8.82 + xmlChar *type=NULL;
8.83 + xmlChar *text;
8.84 + for(field=node->xmlChildrenNode;field;field=field->next)
8.85 + if (!xmlStrcmp(field->name,(const xmlChar *)"field"))
8.86 + {
8.87 + for(attr=field->properties;attr;attr=attr->next)
8.88 + if (!xmlStrcmp(attr->name,(const xmlChar *)"name"))
8.89 + {
8.90 + name=xmlNodeListGetString(doc,attr->children,1);
8.91 + if (!xmlStrcmp(name,(const xmlChar *)"artifact_id"))
8.92 + id=xmlNodeListGetString(doc,field->children,1);
8.93 + if (!xmlStrcmp(name,(const xmlChar *)"artifact_type"))
8.94 + type=xmlNodeListGetString(doc,field->children,1);
8.95 + }
8.96 + if (!xmlStrcmp(name,(const xmlChar *)"artifact_history"))
8.97 + for(cur=field->xmlChildrenNode;cur;cur=cur->next)
8.98 + if (!xmlStrcmp(cur->name,"history"))
8.99 + parse_history(doc,id,type,cur);
8.100 + if (name)
8.101 + {
8.102 + xmlFree(name);
8.103 + name=NULL;
8.104 + }
8.105 + }
8.106 + if (id)
8.107 + xmlFree(id);
8.108 + if (type)
8.109 + xmlFree(type);
8.110 +}
8.111 +
8.112 +int main(int argc,char **argv)
8.113 +{
8.114 + xmlDocPtr doc;
8.115 + xmlNodePtr cur;
8.116 + doc=xmlParseFile(argv[1]);
8.117 + if (!doc)
8.118 + {
8.119 + fprintf(stderr,"Document not parsed successfully.\n");
8.120 + exit(1);
8.121 + }
8.122 + cur=xmlDocGetRootElement(doc);
8.123 + if (!cur)
8.124 + {
8.125 + fprintf(stderr,"Empty document.\n");
8.126 + xmlFreeDoc(doc);
8.127 + exit(1);
8.128 + }
8.129 + if (xmlStrcmp(cur->name,(const xmlChar *)"project_export"))
8.130 + {
8.131 + fprintf(stderr,"%s does not appear to be a project export document.\n",
8.132 + argv[1]);
8.133 + xmlFreeDoc(doc);
8.134 + exit(1);
8.135 + }
8.136 + for(cur=cur->xmlChildrenNode;cur;cur=cur->next)
8.137 + if (!xmlStrcmp(cur->name,(const xmlChar *)"artifacts"))
8.138 + break;
8.139 + if (!cur)
8.140 + {
8.141 + fprintf(stderr,"%s does not appear to contain an artifacts node.\n",
8.142 + argv[1]);
8.143 + xmlFreeDoc(doc);
8.144 + exit(1);
8.145 + }
8.146 + for(cur=cur->xmlChildrenNode;cur;cur=cur->next)
8.147 + if (!xmlStrcmp(cur->name,(const xmlChar *)"artifact"))
8.148 + parse_artifact(doc,cur);
8.149 + xmlFreeDoc(doc);
8.150 + exit(0);
8.151 +}
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/get_users.xsl Thu Nov 16 08:30:26 2006 +0000
9.3 @@ -0,0 +1,39 @@
9.4 +<?xml version="1.0"?>
9.5 +<xsl:stylesheet version="1.0"
9.6 + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
9.7 + xmlns:exsl="http://exslt.org/common"
9.8 + extension-element-prefixes="exsl">
9.9 +
9.10 +<xsl:variable name="users">
9.11 + <xsl:for-each select="//artifact/field[@name='submitted_by' or @name='assigned_to']">
9.12 + <user><xsl:value-of select="normalize-space(.)"/></user>
9.13 + </xsl:for-each>
9.14 + <xsl:for-each select="//artifact/field[@name='artifact_history']/history/field[@name='mod_by']">
9.15 + <user><xsl:value-of select="normalize-space(.)"/></user>
9.16 + </xsl:for-each>
9.17 + <xsl:for-each select="//artifact/field[@name='artifact_messages']/message/field[@name='user_name']">
9.18 + <user><xsl:value-of select="normalize-space(.)"/></user>
9.19 + </xsl:for-each>
9.20 +</xsl:variable>
9.21 +
9.22 +<xsl:variable name="sorted_users">
9.23 + <xsl:for-each select="exsl:node-set($users)/user">
9.24 + <xsl:sort/>
9.25 + <xsl:copy-of select="."/>
9.26 + </xsl:for-each>
9.27 +</xsl:variable>
9.28 +
9.29 +<xsl:template match="/">
9.30 + <users>
9.31 + <xsl:for-each select="exsl:node-set($sorted_users)/user">
9.32 + <xsl:if test="not(. = following-sibling::user[1])">
9.33 + <user>
9.34 + <xsl:value-of select="."/>
9.35 + <xsl:text>@users.sourceforge.net</xsl:text>
9.36 + </user>
9.37 + </xsl:if>
9.38 + </xsl:for-each>
9.39 + </users>
9.40 +</xsl:template>
9.41 +
9.42 +</xsl:stylesheet>
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/sf2bz.sh Thu Nov 16 08:30:26 2006 +0000
10.3 @@ -0,0 +1,109 @@
10.4 +#!/bin/sh
10.5 +if [ $# -ne 2 ]; then
10.6 + echo "Usage: sf2bz <sf-project-export>.xml <bz-import>.xml" >&2
10.7 + exit 1
10.8 +fi
10.9 +input=$1
10.10 +output=$2
10.11 +topdir=/home/ali/wk/slashem/web.scripts
10.12 +cd $topdir
10.13 +cache_dir=$topdir/cache
10.14 +tagsoup=tagsoup/tagsoup-1.0.1.jar
10.15 +#tmpdir=`mktemp -d /tmp/XXXXXXXX` || exit 1
10.16 +tmpdir=/tmp/sf2bz; rm -rf $tmpdir; mkdir $tmpdir
10.17 +mkdir -p $cache_dir/sf/artifacts
10.18 +# Sourceforge's export is pretty good, but misses a few things.
10.19 +# Deal with one of these (descriptions for attached files) here.
10.20 +echo '<?xml version="1.0"?>' > $tmpdir/descriptions.xml
10.21 +echo '<artifacts xmlns:xi="http://www.w3.org/2001/XInclude">' \
10.22 + >> $tmpdir/descriptions.xml
10.23 +./get_attached_files $input | cut -s "-d " -f 1,2 | sort -n | uniq | \
10.24 + while read atid aid; do
10.25 + mkdir -p $cache_dir/sf/artifacts/$atid
10.26 + if [ ! -r $cache_dir/sf/artifacts/$atid/$aid.html ]; then
10.27 + wget -q -O $cache_dir/sf/artifacts/$atid/$aid.html \
10.28 + "http://sourceforge.net/tracker/index.php?func=detail&aid=$aid&group_id=9746&atid=$atid"
10.29 + fi
10.30 + mkdir -p $cache_dir/artifacts/$atid
10.31 + if [ ! -r $cache_dir/artifacts/$atid/$aid.xhtml ]; then
10.32 + java -jar $tagsoup $cache_dir/sf/artifacts/$atid/$aid.html \
10.33 + > $cache_dir/artifacts/$atid/$aid.xhtml
10.34 + fi
10.35 + echo "<xi:include href=\"$cache_dir/artifacts/$atid/$aid.xhtml\"/>" >> $tmpdir/descriptions.xml
10.36 +done
10.37 +echo '</artifacts>' >> $tmpdir/descriptions.xml
10.38 +xsltproc --xinclude attachment_descs.xsl $tmpdir/descriptions.xml \
10.39 + > $tmpdir/attachment-descriptions.xml
10.40 +# Some details of attachments are included in the project export.
10.41 +xsltproc attachment_details.xsl $input > $tmpdir/attachment-details.xml
10.42 +# Then convert the attachments to XML (as required by bugzilla)
10.43 +# pulling in the various bits of information we have gathered.
10.44 +mkdir -p $cache_dir/sf/attachments $cache_dir/attachments
10.45 +mkdir -p $tmpdir/jail
10.46 +./get_attached_files artifacts.xml | while read atid aid file_id; do
10.47 + if [ ! -r $cache_dir/sf/attachments/$file_id ]; then
10.48 + wget -N -O $cache_dir/sf/attachments/$file_id "http://sourceforge.net/tracker/download.php?group_id=9746&atid=$atid&file_id=$file_id&aid=$aid"
10.49 + fi
10.50 + if [ ! -r $cache_dir/attachments/$file_id.xml ]; then
10.51 + type=`file -bi $cache_dir/sf/attachments/$file_id | sed 's/;.*//'`
10.52 + case "$type" in
10.53 + text/*)
10.54 + (cd $tmpdir/jail; patch --dry-run -s -f \
10.55 + < $cache_dir/sf/attachments/$file_id > /dev/null 2>&1)
10.56 + if [ $? -lt 2 ]; then
10.57 + pflag="-p"
10.58 + else
10.59 + pflag=""
10.60 + fi
10.61 + ;;
10.62 + *)
10.63 + pflag=""
10.64 + ;;
10.65 + esac
10.66 + ./xml_attached_file $pflag -i $file_id -t $type \
10.67 + $cache_dir/sf/attachments/$file_id > $tmpdir/$file_id.1.xml
10.68 + xsltproc attachment_gather.xsl $tmpdir/$file_id.1.xml \
10.69 + > $tmpdir/$file_id.2.xml
10.70 + xsltproc --xinclude attachment_post.xsl $tmpdir/$file_id.2.xml \
10.71 + > $cache_dir/attachments/$file_id.xml
10.72 + fi
10.73 +done
10.74 +# We can't get hold of descriptions of deleted attachments, so we
10.75 +# keep a copy of the file in case we need it. Ideally, we'd make sure
10.76 +# that we only add descriptions, but that's for another day.
10.77 +cp $tmpdir/attachment-descriptions.xml $topdir
10.78 +xsltproc get_users.xsl $input > $tmpdir/raw_users.xml
10.79 +echo '<artifacts xmlns:xi="http://www.w3.org/2001/XInclude">' \
10.80 + >> $tmpdir/users.1.xml
10.81 +for file in $cache_dir/artifacts/*/*.xhtml; do
10.82 + echo "<xi:include href=\"$file\"/>" >> $tmpdir/users.1.xml
10.83 +done
10.84 +echo "<xi:include href=\"raw_users.xml\"/>" >> $tmpdir/users.1.xml
10.85 +echo '</artifacts>' >> $tmpdir/users.1.xml
10.86 +xsltproc --xinclude user_names.xsl $tmpdir/users.1.xml > $tmpdir/users.2.xml
10.87 +mkdir -p $cache_dir/sf/users $cache_dir/users
10.88 +echo '<sf_users xmlns:xi="http://www.w3.org/2001/XInclude">' \
10.89 + >> $tmpdir/users.3.xml
10.90 +xsltproc unknown_users.xsl $tmpdir/users.2.xml | while read user; do
10.91 + case $user in
10.92 + *@users.sourceforge.net)
10.93 + user_id=`echo $user | sed 's/@users.sourceforge.net$//'`
10.94 + if [ ! -r $cache_dir/sf/users/$user_id.html ]; then
10.95 + wget -N -O $cache_dir/sf/users/$user_id.html \
10.96 + "http://sourceforge.net/users/$user_id/"
10.97 + fi
10.98 + if [ ! -r $cache_dir/users/$user_id.xhtml ]; then
10.99 + java -jar $tagsoup $cache_dir/sf/users/$user_id.html \
10.100 + > $cache_dir/users/$user_id.xhtml
10.101 + fi
10.102 + echo "<xi:include href=\"$cache_dir/users/$user_id.xhtml\"/>" \
10.103 + >> $tmpdir/users.3.xml
10.104 + esac
10.105 +done
10.106 +echo "<xi:include href=\"users.2.xml\"/>" >> $tmpdir/users.3.xml
10.107 +echo '</sf_users>' >> $tmpdir/users.3.xml
10.108 +xsltproc --xinclude user_names.xsl $tmpdir/users.3.xml > users.xml
10.109 +ln -s $cache_dir/attachments $tmpdir
10.110 +xsltproc bugzilla.xsl $input > artifacts.tmp.xml
10.111 +xsltproc --xinclude bugzilla_post.xsl artifacts.tmp.xml > $output
10.112 +#rm -rf $tmpdir artifacts.tmp.xml
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
11.2 +++ b/sf2bz.xsl Thu Nov 16 08:30:26 2006 +0000
11.3 @@ -0,0 +1,29 @@
11.4 +<?xml version="1.0"?>
11.5 +
11.6 +<!-- A set of templates used in multiple stylesheets -->
11.7 +
11.8 +<xsl:stylesheet version="1.0"
11.9 + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
11.10 + xmlns:date="http://exslt.org/dates-and-times"
11.11 + extension-element-prefixes="date">
11.12 +
11.13 +<!-- Convert a UNIX-style date (seconds since 1 Jan 1970) to
11.14 + the timestamp format used by bugzilla: %Y-%m-%d %X -->
11.15 +<xsl:template name="bugzilla-timestamp">
11.16 + <xsl:param name="secs" select="."/>
11.17 + <xsl:variable name="dt" select="date:add('1970',concat('PT',$secs,'S'))"/>
11.18 + <xsl:value-of select="date:year($dt)"/>
11.19 + <xsl:text>-</xsl:text>
11.20 + <xsl:value-of select="format-number(date:month-in-year($dt),'00')"/>
11.21 + <xsl:text>-</xsl:text>
11.22 + <xsl:value-of select="format-number(date:day-in-month($dt),'00')"/>
11.23 + <xsl:text> </xsl:text>
11.24 + <xsl:value-of select="format-number(date:hour-in-day($dt),'00')"/>
11.25 + <xsl:text>:</xsl:text>
11.26 + <xsl:value-of select="format-number(date:minute-in-hour($dt),'00')"/>
11.27 + <xsl:text>:</xsl:text>
11.28 + <xsl:value-of select="format-number(date:second-in-minute($dt),'00')"/>
11.29 + <xsl:text> UTC</xsl:text>
11.30 +</xsl:template>
11.31 +
11.32 +</xsl:stylesheet>
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/tagsoup/SRC Thu Nov 16 08:30:26 2006 +0000
12.3 @@ -0,0 +1,2 @@
12.4 +http://mercury.ccil.org/~cowan/XML/tagsoup/
12.5 +http://mercury.ccil.org/~cowan/XML/tagsoup/tagsoup-1.0.1.jar
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
13.2 +++ b/unknown_users.xsl Thu Nov 16 08:30:26 2006 +0000
13.3 @@ -0,0 +1,15 @@
13.4 +<?xml version="1.0"?>
13.5 +
13.6 +<xsl:stylesheet version="1.0"
13.7 + xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
13.8 +
13.9 +<xsl:output method="text" encoding="utf-8"/>
13.10 +
13.11 +<xsl:template match="//users/user[not(@name)]">
13.12 + <xsl:value-of select="."/>
13.13 + <xsl:text> </xsl:text>
13.14 +</xsl:template>
13.15 +
13.16 +<xsl:template match="text()"/>
13.17 +
13.18 +</xsl:stylesheet>
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
14.2 +++ b/user_names.xsl Thu Nov 16 08:30:26 2006 +0000
14.3 @@ -0,0 +1,61 @@
14.4 +<?xml version="1.0"?>
14.5 +
14.6 +<xsl:stylesheet version="1.0"
14.7 + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
14.8 + xmlns:xhtml="http://www.w3.org/1999/xhtml"
14.9 + xmlns:exsl="http://exslt.org/common"
14.10 + extension-element-prefixes="exsl"
14.11 + exclude-result-prefixes="xhtml">
14.12 +
14.13 +<xsl:variable name="users">
14.14 + <xsl:for-each select="//users/user">
14.15 + <user>
14.16 + <xsl:copy-of select="@name"/>
14.17 + <xsl:value-of select="."/>
14.18 + </user>
14.19 + </xsl:for-each>
14.20 + <xsl:for-each select="//xhtml:td[xhtml:b='Submitted By:' and xhtml:a]">
14.21 + <xsl:element name="user">
14.22 + <xsl:attribute name="name">
14.23 + <xsl:for-each select="text()">
14.24 + <xsl:if test="contains(., '-')">
14.25 + <xsl:value-of select="normalize-space(substring-before(., '-'))"/>
14.26 + </xsl:if>
14.27 + </xsl:for-each>
14.28 + </xsl:attribute>
14.29 + <xsl:value-of select="xhtml:a"/>
14.30 + <xsl:text>@users.sourceforge.net</xsl:text>
14.31 + </xsl:element>
14.32 + </xsl:for-each>
14.33 + <xsl:for-each select="//xhtml:table[xhtml:tr/xhtml:td[normalize-space(.)='Login Name:'] and xhtml:tr/xhtml:td[normalize-space(.)='Publicly Displayed Name:']]">
14.34 + <xsl:element name="user">
14.35 + <xsl:attribute name="name">
14.36 + <xsl:value-of select="normalize-space(xhtml:tr/xhtml:td[normalize-space(.)='Publicly Displayed Name:']/following-sibling::xhtml:td)"/>
14.37 + </xsl:attribute>
14.38 + <xsl:value-of select="normalize-space(xhtml:tr/xhtml:td[normalize-space(.)='Login Name:']/following-sibling::xhtml:td)"/>
14.39 + <xsl:text>@users.sourceforge.net</xsl:text>
14.40 + </xsl:element>
14.41 + </xsl:for-each>
14.42 +</xsl:variable>
14.43 +
14.44 +<xsl:variable name="sorted_users">
14.45 + <xsl:for-each select="exsl:node-set($users)/user">
14.46 + <xsl:sort/>
14.47 + <xsl:copy-of select="."/>
14.48 + </xsl:for-each>
14.49 +</xsl:variable>
14.50 +
14.51 +<xsl:template match="/">
14.52 + <users>
14.53 + <xsl:for-each select="exsl:node-set($sorted_users)/user">
14.54 + <xsl:if test="not(. = following-sibling::user[1])">
14.55 + <xsl:copy>
14.56 + <xsl:copy-of select="@name"/>
14.57 + <xsl:value-of select="."/>
14.58 + </xsl:copy>
14.59 + </xsl:if>
14.60 + </xsl:for-each>
14.61 + </users>
14.62 +</xsl:template>
14.63 +
14.64 +</xsl:stylesheet>
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
15.2 +++ b/xml_attached_file.c Thu Nov 16 08:30:26 2006 +0000
15.3 @@ -0,0 +1,144 @@
15.4 +/*
15.5 + * This program converts an attached file (which has previously been
15.6 + * downloaded from sourceforge) into bugzilla-style XML.
15.7 + */
15.8 +
15.9 +#include <stdlib.h>
15.10 +#include <stdio.h>
15.11 +#include <string.h>
15.12 +#ifdef __linux__
15.13 +#include <getopt.h>
15.14 +#endif
15.15 +
15.16 +#ifdef __linux__
15.17 +static struct option long_options[] = {
15.18 + {"type", 1, 0, 't'},
15.19 + {"id", 1, 0, 'i'},
15.20 + {"patch", 0, 0, 'p'},
15.21 + {0, 0, 0, 0}
15.22 +};
15.23 +
15.24 +static char *long_option_str(char opt)
15.25 +{
15.26 + int i;
15.27 + static char buf[100];
15.28 + for(i=0;long_options[i].name;i++)
15.29 + if (long_options[i].val==opt)
15.30 + {
15.31 + sprintf(buf,", --%s",long_options[i].name);
15.32 + return buf;
15.33 + }
15.34 + return "";
15.35 +}
15.36 +#else
15.37 +#define long_option_str(opt) ""
15.38 +#endif
15.39 +
15.40 +usage()
15.41 +{
15.42 + fprintf(stderr,"Usage: xml_attached_file [OPTION] file\n");
15.43 + fprintf(stderr,"\n");
15.44 + fprintf(stderr," -t%s=MIME-type MIME type to use\n",
15.45 + long_option_str('t'));
15.46 + fprintf(stderr," -i%s=ID Attachment ID to use\n",
15.47 + long_option_str('i'));
15.48 + fprintf(stderr," -p%s Mark attachment as a patch\n",
15.49 + long_option_str('p'));
15.50 + exit(1);
15.51 +}
15.52 +
15.53 +/* Note that we limit line lengths to 76 characters following RFC 2045
15.54 + * (bugzilla uses MIME::Base64). This isn't strictly compliant with RFC 4648.
15.55 + */
15.56 +static void base64_encode(FILE *in,FILE *out)
15.57 +{
15.58 + int ng=0; /* 76 characters == 19 groups */
15.59 + size_t nb;
15.60 + unsigned char bytes[3];
15.61 + const char alphabet[64]=
15.62 + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
15.63 + while ((nb=fread(bytes,1,3,in))==3)
15.64 + {
15.65 + if (ng++>=19)
15.66 + {
15.67 + putc('\n',out);
15.68 + ng=1;
15.69 + }
15.70 + putc(alphabet[bytes[0]>>2],out);
15.71 + putc(alphabet[(bytes[0]<<4|bytes[1]>>4)&0x3F],out);
15.72 + putc(alphabet[(bytes[1]<<2|bytes[2]>>6)&0x3F],out);
15.73 + putc(alphabet[bytes[2]&0x3F],out);
15.74 + }
15.75 + if (nb)
15.76 + {
15.77 + if (ng>=19)
15.78 + putc('\n',out);
15.79 + putc(alphabet[bytes[0]>>2],out);
15.80 + if (nb==2)
15.81 + {
15.82 + putc(alphabet[(bytes[0]<<4|bytes[1]>>4)&0x3F],out);
15.83 + putc(alphabet[bytes[1]<<2&0x3F],out);
15.84 + }
15.85 + else
15.86 + {
15.87 + putc(alphabet[bytes[0]<<4&0x3F],out);
15.88 + putc('=',out);
15.89 + }
15.90 + putc('=',out);
15.91 + }
15.92 +}
15.93 +
15.94 +int main(int argc,char **argv)
15.95 +{
15.96 + int c;
15.97 + FILE *fp;
15.98 + unsigned long id=0;
15.99 + int ispatch=0;
15.100 + char *type=NULL;
15.101 + for(;;)
15.102 + {
15.103 +#ifdef __linux__
15.104 + int option_index=0;
15.105 + c=getopt_long(argc,argv,"t:i:p",long_options,&option_index);
15.106 +#else
15.107 + c=getopt(argc,argv,"t:i:p");
15.108 +#endif
15.109 + if (c<0)
15.110 + break;
15.111 + switch(c)
15.112 + {
15.113 + case 't':
15.114 + type=optarg;
15.115 + break;
15.116 + case 'i':
15.117 + id=strtoul(optarg,NULL,10);
15.118 + break;
15.119 + case 'p':
15.120 + ispatch=1;
15.121 + break;
15.122 + default:
15.123 + usage();
15.124 + }
15.125 + }
15.126 + if (optind<argc)
15.127 + {
15.128 + fp=fopen(argv[optind],"rb");
15.129 + if (!fp)
15.130 + {
15.131 + perror(argv[optind]);
15.132 + exit(1);
15.133 + }
15.134 + }
15.135 + else
15.136 + fp=stdin;
15.137 + printf("<attachment ispatch=\"%d\">\n",ispatch);
15.138 + if (id)
15.139 + printf(" <attachid>%lu</attachid>\n",id);
15.140 + if (type)
15.141 + printf(" <type>%s</type>\n",type);
15.142 + printf(" <data encoding=\"base64\">\n");
15.143 + base64_encode(fp,stdout);
15.144 + printf("\n </data>\n");
15.145 + printf("</attachment>\n");
15.146 + exit(0);
15.147 +}