jf2atom.c - jfconvert - JSON Feed (subset) to sfeed or Atom converter
 (HTM) git clone git://git.codemadness.org/jfconvert
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       jf2atom.c (7078B)
       ---
            1 #include <errno.h>
            2 #include <stdio.h>
            3 #include <stdlib.h>
            4 #include <string.h>
            5 
            6 #ifdef __OpenBSD__
            7 #include <unistd.h>
            8 #else
            9 #define pledge(a,b) 0
           10 #endif
           11 
           12 #include "json.h"
           13 
           14 /* control-character in the ASCII range 0-127: compatible with UTF-8 */
           15 #define ISCNTRL(c) ((c) < ' ' || (c) == 0x7f)
           16 
           17 /* compare attributes case-sensitively */
           18 #define attrcmp strcmp
           19 
           20 static int itemisopen = 0, enclosureisopen = 0;
           21 
           22 /* Escape characters below as HTML 2.0 / XML 1.0. */
           23 void
           24 xmlencode(const char *s, FILE *fp)
           25 {
           26         for (; *s; ++s) {
           27                 switch (*s) {
           28                 case '<':  fputs("&lt;",   fp); break;
           29                 case '>':  fputs("&gt;",   fp); break;
           30                 case '\'': fputs("&#39;",  fp); break;
           31                 case '&':  fputs("&amp;",  fp); break;
           32                 case '"':  fputs("&quot;", fp); break;
           33                 default:   putc(*s, fp);
           34                 }
           35         }
           36 }
           37 
           38 void
           39 processnode(struct json_node *nodes, size_t depth, const char *value, size_t valuelen)
           40 {
           41         const char *outtag, *outtype, *outhref;
           42 
           43         /* feed / channel */
           44         if (depth == 2) {
           45                 if (nodes[0].type == JSON_TYPE_OBJECT) {
           46                         if (nodes[1].type == JSON_TYPE_STRING) {
           47                                 if (!attrcmp(nodes[1].name, "title")) {
           48                                         fputs("<title type=\"text\">", stdout);
           49                                         xmlencode(value, stdout);
           50                                         fputs("</title>\n", stdout);
           51                                 } else if (!attrcmp(nodes[1].name, "home_page_url")) {
           52                                         fputs("<link rel=\"alternate\" type=\"text/html\" href=\"", stdout);
           53                                         xmlencode(value, stdout);
           54                                         fputs("\" />\n", stdout);
           55                                 } else if (!attrcmp(nodes[1].name, "description")) {
           56                                         fputs("<subtitle>", stdout);
           57                                         xmlencode(value, stdout);
           58                                         fputs("</subtitle>\n", stdout);
           59                                 }
           60                         }
           61                 }
           62         }
           63 
           64         /* item */
           65         if (depth == 3) {
           66                 if (nodes[0].type == JSON_TYPE_OBJECT &&
           67                     nodes[1].type == JSON_TYPE_ARRAY &&
           68                     nodes[2].type == JSON_TYPE_OBJECT &&
           69                     !attrcmp(nodes[1].name, "items")) {
           70                         if (enclosureisopen) {
           71                                 fputs(" />\n", stdout);
           72                                 enclosureisopen = 0;
           73                         }
           74                         if (itemisopen)
           75                                 fputs("</entry>\n", stdout);
           76                         fputs("<entry>\n", stdout);
           77                         itemisopen = 1;
           78                 }
           79         }
           80 
           81         /* item attributes */
           82         if (depth == 4) {
           83                 if (nodes[0].type == JSON_TYPE_OBJECT &&
           84                     nodes[1].type == JSON_TYPE_ARRAY &&
           85                     nodes[2].type == JSON_TYPE_OBJECT &&
           86                     !attrcmp(nodes[1].name, "items")) {
           87                         outtag = NULL;
           88                         outtype = NULL;
           89                         outhref = NULL;
           90 
           91                         if (!attrcmp(nodes[3].name, "content_html")) {
           92                                 outtag = "content";
           93                                 outtype = "html";
           94                         } else if (!attrcmp(nodes[3].name, "content_text")) {
           95                                 outtag = "content";
           96                                 outtype = "text";
           97                         } else if (!attrcmp(nodes[3].name, "date_published")) {
           98                                 outtag = "published";
           99                         } else if (!attrcmp(nodes[3].name, "date_modified")) {
          100                                 outtag = "updated";
          101                         } else if (!attrcmp(nodes[3].name, "id")) {
          102                                 outtag = "id";
          103                         } else if (!attrcmp(nodes[3].name, "summary")) {
          104                                 outtag = "summary";
          105                         } else if (!attrcmp(nodes[3].name, "title")) {
          106                                 outtag = "title";
          107                         } else if (!attrcmp(nodes[3].name, "url")) {
          108                                 outtag = "link";
          109                                 outhref = value;
          110                                 value = NULL;
          111                         }
          112 
          113                         if (outtag) {
          114                                 fputs("\t<", stdout);
          115                                 fputs(outtag, stdout);
          116                                 if (outhref) {
          117                                         fputs(" href=\"", stdout);
          118                                         xmlencode(outhref, stdout);
          119                                         fputs("\"", stdout);
          120                                 }
          121                                 if (outtype) {
          122                                         fputs(" type=\"", stdout);
          123                                         xmlencode(outtype, stdout);
          124                                         fputs("\"", stdout);
          125                                 }
          126                                 fputs(">", stdout);
          127                                 if (value)
          128                                         xmlencode(value, stdout);
          129                                 fputs("</", stdout);
          130                                 fputs(outtag, stdout);
          131                                 fputs(">\n", stdout);
          132                         }
          133                 }
          134         }
          135 
          136         if (depth == 5) {
          137                 /* 1.0 author name */
          138                 if (nodes[0].type == JSON_TYPE_OBJECT &&
          139                     nodes[1].type == JSON_TYPE_ARRAY &&
          140                     nodes[2].type == JSON_TYPE_OBJECT &&
          141                     nodes[3].type == JSON_TYPE_OBJECT &&
          142                     nodes[4].type == JSON_TYPE_STRING &&
          143                     !attrcmp(nodes[1].name, "items") &&
          144                     !attrcmp(nodes[3].name, "author") &&
          145                     !attrcmp(nodes[4].name, "name")) {
          146                         fputs("\t<author><name>", stdout);
          147                         xmlencode(value, stdout);
          148                         fputs("</name></author>\n", stdout);
          149                 }
          150 
          151                 /* tags / categories */
          152                 if (nodes[0].type == JSON_TYPE_OBJECT &&
          153                     nodes[1].type == JSON_TYPE_ARRAY &&
          154                     nodes[2].type == JSON_TYPE_OBJECT &&
          155                     nodes[3].type == JSON_TYPE_ARRAY &&
          156                     nodes[4].type == JSON_TYPE_STRING &&
          157                     !attrcmp(nodes[1].name, "items") &&
          158                     !attrcmp(nodes[3].name, "tags")) {
          159                         fputs("\t<category term=\"", stdout);
          160                         xmlencode(value, stdout);
          161                         fputs("\" />\n", stdout);
          162                 }
          163 
          164                 /* enclosure */
          165                 if (nodes[0].type == JSON_TYPE_OBJECT &&
          166                     nodes[1].type == JSON_TYPE_ARRAY &&
          167                     nodes[2].type == JSON_TYPE_OBJECT &&
          168                     nodes[3].type == JSON_TYPE_ARRAY &&
          169                     nodes[4].type == JSON_TYPE_OBJECT &&
          170                     !attrcmp(nodes[1].name, "items") &&
          171                     !attrcmp(nodes[3].name, "attachments")) {
          172                         if (enclosureisopen)
          173                                 fputs(" />\n", stdout);
          174                         fputs("\t<link rel=\"enclosure\"", stdout);
          175                         enclosureisopen = 1;
          176                 }
          177         }
          178 
          179         if (depth == 6) {
          180                 /* 1.1 author name */
          181                 if (nodes[0].type == JSON_TYPE_OBJECT &&
          182                     nodes[1].type == JSON_TYPE_ARRAY &&
          183                     nodes[2].type == JSON_TYPE_OBJECT &&
          184                     nodes[3].type == JSON_TYPE_ARRAY &&
          185                     nodes[4].type == JSON_TYPE_OBJECT &&
          186                     nodes[5].type == JSON_TYPE_STRING &&
          187                     !attrcmp(nodes[1].name, "items") &&
          188                     !attrcmp(nodes[3].name, "authors") &&
          189                     !attrcmp(nodes[5].name, "name")) {
          190                         fputs("\t<author><name>", stdout);
          191                         xmlencode(value, stdout);
          192                         fputs("</name></author>\n", stdout);
          193                 }
          194 
          195                 /* enclosure attributes */
          196                 if (nodes[0].type == JSON_TYPE_OBJECT &&
          197                     nodes[1].type == JSON_TYPE_ARRAY &&
          198                     nodes[2].type == JSON_TYPE_OBJECT &&
          199                     nodes[3].type == JSON_TYPE_ARRAY &&
          200                     nodes[4].type == JSON_TYPE_OBJECT &&
          201                     (nodes[5].type == JSON_TYPE_STRING || nodes[5].type == JSON_TYPE_NUMBER) &&
          202                     !attrcmp(nodes[1].name, "items") &&
          203                     !attrcmp(nodes[3].name, "attachments")) {
          204                         if (!attrcmp(nodes[5].name, "url")) {
          205                                 fputs(" href=\"", stdout);
          206                                 xmlencode(value, stdout);
          207                                 fputs("\"", stdout);
          208                         } else if (!attrcmp(nodes[5].name, "mime_type")) {
          209                                 fputs(" type=\"", stdout);
          210                                 xmlencode(value, stdout);
          211                                 fputs("\"", stdout);
          212                         } else if (!attrcmp(nodes[5].name, "size_in_bytes")) {
          213                                 fputs(" length=\"", stdout);
          214                                 xmlencode(value, stdout);
          215                                 fputs("\"", stdout);
          216                         }
          217                 }
          218         }
          219 
          220         if (ferror(stdout)) {
          221                 fprintf(stderr, "write error: <stdout>\n");
          222                 exit(2);
          223         }
          224 }
          225 
          226 int
          227 main(int argc, char *argv[])
          228 {
          229         if (pledge("stdio", NULL) == -1) {
          230                 fprintf(stderr, "pledge stdio: %s\n", strerror(errno));
          231                 return 1;
          232         }
          233 
          234         fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
          235               "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n", stdout);
          236 
          237         switch (parsejson(processnode)) {
          238         case JSON_ERROR_MEM:
          239                 fputs("error: cannot allocate enough memory\n", stderr);
          240                 return 2;
          241         case JSON_ERROR_INVALID:
          242                 fputs("error: invalid JSON\n", stderr);
          243                 return 1;
          244         }
          245 
          246         if (enclosureisopen)
          247                 fputs(" />\n", stdout);
          248         if (itemisopen)
          249                 fputs("</entry>\n", stdout);
          250         fputs("</feed>\n", stdout);
          251 
          252         if (ferror(stdin)) {
          253                 fprintf(stderr, "read error: <stdin>\n");
          254                 return 2;
          255         }
          256         if (fflush(stdout) || ferror(stdout)) {
          257                 fprintf(stderr, "write error: <stdout>\n");
          258                 return 2;
          259         }
          260 
          261         return 0;
          262 }