Improve detection of the bindings
[src/app-framework-binder.git] / src / devtools / genskel.c
1 /*
2  * Copyright (C) 2016-2019 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 /*
18  * This simple program expands the object { "$ref": "#/path/to/a/target" }
19  *
20  * For example:
21  *
22  *  {
23  *    "type":{
24  *      "a": "int",
25  *      "b": { "$ref": "#/type/a" }
26  *    }
27  *  }
28  *
29  * will be exapanded to
30  *
31  *  {
32  *    "type":{
33  *      "a": "int",
34  *      "b": "int"
35  *    }
36  *  }
37  *
38  * Invocation:   program  [file|-]...
39  *
40  * without arguments, it reads the input.
41  */
42
43 #define _GNU_SOURCE
44 #include <string.h>
45 #include <stdio.h>
46 #include <unistd.h>
47 #include <ctype.h>
48
49 #include <json-c/json.h>
50
51 #define T(x)   ((x) && *(x))
52 #define oom(x) do{if(!(x)){fprintf(stderr,"out of memory\n");exit(1);}}while(0)
53
54 /**
55  * records path to the expanded node
56  */
57 struct path
58 {
59         struct json_object *object;     /**< node being expanded */
60         struct path *upper;             /**< link to upper expanded nodes */
61 };
62
63 /**
64  * root of the JSON being parsed
65  */
66 int version = 3;
67 struct json_object *root = NULL;
68 struct json_object *d_perms = NULL;
69 struct json_object *a_perms = NULL;
70 const char *preinit = NULL;
71 const char *init = NULL;
72 const char *onevent = NULL;
73 const char *api = NULL;
74 const char *scope = NULL;
75 const char *prefix = NULL;
76 const char *postfix = NULL;
77 const char *provideclass = NULL;
78 const char *requireclass = NULL;
79 const char *requireapi = NULL;
80 char *capi = NULL;
81 int priv = -1;
82 int noconc = -1;
83 int cpp = 0;
84
85 /**
86  * Search for a reference of type "#/a/b/c" in the
87  * parsed JSON object (root)
88  */
89 struct json_object *search(const char *path)
90 {
91         char *d;
92         struct json_object *i;
93
94         /* does it match #/ at the beginning? */
95         if (path[0] != '#' || (path[0] && path[1] != '/'))
96                 return NULL;
97
98         /* search from root to target */
99         i = root;
100         d = strdupa(path+2);
101         d = strtok(d, "/");
102         while(i && d) {
103                 if (!json_object_object_get_ex(i, d, &i))
104                         return NULL;
105                 d = strtok(NULL, "/");
106         }
107         return i;
108 }
109
110 /**
111  * Expands the node designated by path and returns its expanded form
112  */
113 struct json_object *expand_$ref(struct path path)
114 {
115         struct path *p;
116         struct json_object *o, *x;
117         int n, i;
118         struct json_object_iterator ji, jn;
119
120         /* expansion depends of the type of the node */
121         switch (json_object_get_type(path.object)) {
122         case json_type_object:
123                 /* for object, look if it contains a property "$ref" */
124                 if (json_object_object_get_ex(path.object, "$ref", &o)) {
125                         /* yes, reference, try to substitute its target */
126                         if (!json_object_is_type(o, json_type_string)) {
127                                 fprintf(stderr, "found a $ref not being string. Is: %s\n", json_object_get_string(o));
128                                 exit(1);
129                         }
130                         x = search(json_object_get_string(o));
131                         if (!x) {
132                                 fprintf(stderr, "$ref not found. Was: %s\n", json_object_get_string(o));
133                                 exit(1);
134                         }
135                         p = &path;
136                         while(p) {
137                                 if (x == p->object) {
138                                         fprintf(stderr, "$ref recursive. Was: %s\n", json_object_get_string(o));
139                                         exit(1);
140                                 }
141                                 p = p->upper;
142                         }
143                         /* cool found, return a new instance of the target */
144                         return json_object_get(x);
145                 }
146                 /* no, expand the values */
147                 ji = json_object_iter_begin(path.object);
148                 jn = json_object_iter_end(path.object);
149                 while (!json_object_iter_equal(&ji, &jn)) {
150                         o = json_object_iter_peek_value(&ji);
151                         x = expand_$ref((struct path){ .object = o, .upper = &path });
152                         if (x != o)
153                                 json_object_object_add(path.object, json_object_iter_peek_name(&ji), x);
154                         json_object_iter_next(&ji);
155                 }
156                 break;
157         case json_type_array:
158                 /* expand the values of arrays */
159                 i = 0;
160                 n = (int)json_object_array_length(path.object);
161                 while (i != n) {
162                         o = json_object_array_get_idx(path.object, i);
163                         x = expand_$ref((struct path){ .object = o, .upper = &path });
164                         if (x != o)
165                                 json_object_array_put_idx(path.object, i, x);
166                         i++;
167                 }
168                 break;
169         default:
170                 /* otherwise no expansion */
171                 break;
172         }
173
174         /* return the given node */
175         return path.object;
176 }
177
178 /* create c name by replacing non alpha numeric characters with underscores */
179 char *cify(const char *str)
180 {
181         char *r = strdup(str);
182         int i = 0;
183         while (r && r[i]) {
184                 if (!isalnum(r[i]))
185                         r[i] = '_';
186                 i++;
187         }
188         return r;
189 }
190
191 /* format the specification as a C string */
192 char *make_info(const char *text, int split)
193 {
194         const char *a, *b;
195         char *desc, c, buf[3] = {0};
196         size_t len;
197         int i, pos, e;
198
199         /* estimated length */
200         a = b = text;
201         len = 1;
202         while((c = *b++)) {
203                 len += 1 + ('"' == c);
204         }
205
206         len += 7 * (1 + len / 72);
207         desc = malloc(len);
208         oom(desc);
209
210         len = pos = 0;
211         if (!split)
212                 desc[len++] = '"';
213         b = a;
214         while((c = *b++)) {
215                 if (c == '"') {
216                         buf[0] = '\\';
217                         buf[1] = '"';
218                         buf[2] = 0;
219                 }
220                 else if (c == '\\') {
221                         switch ((c = *b++)) {
222                         case 0:
223                                 b--;
224                                 buf[0] = 0;
225                                 break;
226                         case '/':
227                                 buf[0] = '/';
228                                 buf[1] = 0;
229                                 break;
230                         default:
231                                 buf[0] = '\\';
232                                 buf[1] = c;
233                                 buf[2] = 0;
234                                 break;
235                         }
236                 }
237                 else {
238                         buf[0] = c;
239                         buf[1] = 0;
240                 }
241                 i = e = 0;
242                 while (buf[i]) {
243                         if (split) {
244                                 if (pos >= 77 && !e) {
245                                         desc[len++] = '"';
246                                         desc[len++] = '\n';
247                                         pos = 0;
248                                 }
249                                 if (pos == 0) {
250                                         desc[len++] = ' ';
251                                         desc[len++] = ' ';
252                                         desc[len++] = ' ';
253                                         desc[len++] = ' ';
254                                         desc[len++] = '"';
255                                         pos = 5;
256                                 }
257                         }
258                         c = buf[i++];
259                         desc[len++] = c;
260                         e = !e && c == '\\';
261                         pos++;
262                 }
263         }
264         desc[len++] = '"';
265         if (split)
266                 desc[len++] = '\n';
267         desc[len] = 0;
268         return desc;
269 }
270
271 /* make the description of the object */
272 char *make_desc(struct json_object *o)
273 {
274         return make_info(json_object_to_json_string_ext(o, 0), 1);
275 }
276
277 /* get the permission odescription if set */
278 struct json_object *permissions_of_verb(struct json_object *obj)
279 {
280         struct json_object *x, *y;
281
282         if (json_object_object_get_ex(obj, "x-permissions", &x))
283                 return x;
284
285         if (json_object_object_get_ex(obj, "get", &x))
286                 if (json_object_object_get_ex(x, "x-permissions", &y))
287                         return y;
288
289         return NULL;
290 }
291
292 /* output the array of permissions */
293 void print_perms()
294 {
295         int i, n;
296         const char *fmtstr = cpp ? "\t%s" : "\t{ %s }";
297
298         n = a_perms ? (int)json_object_array_length(a_perms) : 0;
299         if (n) {
300                 printf("static const struct afb_auth _afb_auths_%s[] = {\n" , capi);
301                 i = 0;
302                 while (i < n) {
303                         printf(fmtstr, json_object_get_string(json_object_array_get_idx(a_perms, i)));
304                         printf(",\n"+(++i == n));
305                 }
306                 printf("};\n\n");
307         }
308 }
309
310 /*
311  * search in the global object 'd_perm' the computed representation
312  * of the permission described either by 'obj' or 'desc'
313  */
314 struct json_object *new_perm(struct json_object *obj, const char *desc)
315 {
316         const char *tag;
317         char *b;
318         struct json_object *x, *y;
319
320         tag = obj ? json_object_to_json_string_ext(obj, 0) : desc;
321         if (!json_object_object_get_ex(d_perms, tag, &y)) {
322
323                 /* creates the d_perms dico and the a_perms array */
324                 if (!d_perms) {
325                         d_perms = json_object_new_object();
326                         a_perms = json_object_new_array();
327                 }
328
329                 /* creates the reference in the structure */
330                 asprintf(&b, "&_afb_auths_%s[%d]", capi, (int)json_object_array_length(a_perms));
331                 x = json_object_new_string(desc);
332                 y = json_object_new_string(b);
333                 json_object_array_add(a_perms, x);
334                 json_object_object_add(d_perms, tag, y);
335                 free(b);
336         }
337         return y;
338 }
339
340 struct json_object *decl_perm(struct json_object *obj);
341
342 enum optype { And, Or };
343
344 /* recursive declare and/or permissions */
345 struct json_object *decl_perm_a(enum optype op, struct json_object *obj)
346 {
347         int i, n;
348         char *a;
349         const char *opstr, *fmtstr;
350         struct json_object *x, *y;
351
352         if (cpp) {
353                 fmtstr = "afb::auth_%s(%s, %s)";
354                 opstr = op==And ? "and" : "or";
355         } else {
356                 fmtstr = ".type = afb_auth_%s, .first = %s, .next = %s";
357                 opstr = op==And ? "And" : "Or";
358         }
359         x = NULL;
360         i = n = obj ? (int)json_object_array_length(obj) : 0;
361         while (i) {
362                 y = decl_perm(json_object_array_get_idx(obj, --i));
363                 if (!y)
364                         ;
365                 else if (!x)
366                         x = y;
367                 else if (x != y) {
368                         asprintf(&a, fmtstr, opstr, json_object_get_string(y), json_object_get_string(x));
369                         x = new_perm(NULL, a);
370                         free(a);
371                 }
372         }
373         return x;
374 }
375
376 /* declare the permission for obj */
377 struct json_object *decl_perm(struct json_object *obj)
378 {
379         char *a;
380         const char *fmt;
381         struct json_object *x, *y;
382
383         if (json_object_object_get_ex(d_perms, json_object_to_json_string_ext(obj, 0), &x))
384                 return x;
385
386         if (json_object_object_get_ex(obj, "permission", &x)) {
387                 if (cpp)
388                         fmt = "afb::auth_permission(\"%s\")";
389                 else
390                         fmt = ".type = afb_auth_Permission, .text = \"%s\"";
391                 asprintf(&a, fmt, json_object_get_string(x));
392                 y = new_perm(obj, a);
393                 free(a);
394         }
395         else if (json_object_object_get_ex(obj, "anyOf", &x)) {
396                 y = decl_perm_a(Or, x);
397         }
398         else if (json_object_object_get_ex(obj, "allOf", &x)) {
399                 y = decl_perm_a(And, x);
400         }
401         else if (json_object_object_get_ex(obj, "not", &x)) {
402                 x = decl_perm(x);
403                 if (cpp)
404                         fmt = "afb::auth_not(%s)";
405                 else
406                         fmt = ".type = afb_auth_Not, .first = %s";
407                 asprintf(&a, fmt, json_object_get_string(x));
408                 y = new_perm(obj, a);
409                 free(a);
410         }
411         else if (json_object_object_get_ex(obj, "LOA", &x))
412                 y = NULL;
413         else if (json_object_object_get_ex(obj, "session", &x))
414                 y = NULL;
415         else
416                 y = NULL;
417
418         return y;
419 }
420
421 void declare_permissions(const char *name, struct json_object *obj)
422 {
423         struct json_object *p;
424
425         p = permissions_of_verb(obj);
426         if (p)
427                 decl_perm(p);
428 }
429
430
431 #define SESSION_CLOSE  0x000001
432 #define SESSION_RENEW  0x000010
433 #define SESSION_CHECK  0x000100
434 #define SESSION_LOA_1  0x001000
435 #define SESSION_LOA_2  0x011000
436 #define SESSION_LOA_3  0x111000
437 #define SESSION_MASK   0x111111
438
439
440 int get_session(struct json_object *obj);
441
442 int get_session_a(int and, struct json_object *obj)
443 {
444         int i, n, x, y;
445
446         n = obj ? (int)json_object_array_length(obj) : 0;
447         if (n == 0)
448                 return 0;
449
450         i = n;
451         x = get_session(json_object_array_get_idx(obj, --i));
452         while (i) {
453                 y = get_session(json_object_array_get_idx(obj, --i));
454                 if (and)
455                         x &= y;
456                 else
457                         x |= y;
458         }
459         return x;
460 }
461
462 int get_session(struct json_object *obj)
463 {
464         int y;
465         const char *a;
466         struct json_object *x;
467
468         y = 0;
469         if (json_object_object_get_ex(obj, "anyOf", &x)) {
470                 y = get_session_a(1, x);
471         }
472         else if (json_object_object_get_ex(obj, "allOf", &x)) {
473                 y = get_session_a(0, x);
474         }
475         else if (json_object_object_get_ex(obj, "not", &x)) {
476                 y = ~get_session(x) & SESSION_MASK;
477         }
478         else if (json_object_object_get_ex(obj, "LOA", &x)) {
479                 switch (json_object_get_int(x)) {
480                 case 3: y = SESSION_LOA_3; break;
481                 case 2: y = SESSION_LOA_2; break;
482                 case 1: y = SESSION_LOA_1; break;
483                 default: break;
484                 }
485         }
486         else if (json_object_object_get_ex(obj, "session", &x)) {
487                 a = json_object_get_string(x);
488                 if (!strcmp(a, "check"))
489                         y = SESSION_CHECK;
490                 else if (!strcmp(a, "close"))
491                         y = SESSION_CLOSE;
492         }
493         else if (json_object_object_get_ex(obj, "token", &x)) {
494                 a = json_object_get_string(x);
495                 if (!strcmp(a, "refresh"))
496                         y = SESSION_RENEW;
497         }
498
499         return y;
500 }
501
502 void print_session(struct json_object *p)
503 {
504         int s, c, l;
505
506         s = p ? get_session(p) : 0;
507         c = 1;
508         if (s & SESSION_CHECK) {
509                 printf("%s", "|AFB_SESSION_CHECK" + c);
510                 c = 0;
511         }
512         if (s & SESSION_LOA_3 & ~SESSION_LOA_2)
513                 l = 3;
514         else if (s & SESSION_LOA_2 & ~SESSION_LOA_1)
515                 l = 2;
516         else if (s & SESSION_LOA_1)
517                 l = 1;
518         else
519                 l = 0;
520         if (l) {
521                 printf("%s%d", "|AFB_SESSION_LOA_" + c, l);
522                 c = 0;
523         }
524         if (s & SESSION_CLOSE) {
525                 printf("%s", "|AFB_SESSION_CLOSE" + c);
526                 c = 0;
527         }
528         if (s & SESSION_RENEW) {
529                 printf("%s", "|AFB_SESSION_REFRESH" + c);
530                 c = 0;
531         }
532         if (c)
533                 printf("AFB_SESSION_NONE");
534 }
535
536 void print_verb(const char *name)
537 {
538         printf("%s%s%s" , prefix, name, postfix);
539 }
540
541 void print_declare_verb(const char *name, struct json_object *obj)
542 {
543         if (T(scope))
544                 printf("%s ", scope);
545         printf("void ");
546         print_verb(name);
547         printf("(afb_req_t req);\n");
548 }
549
550 void print_struct_verb(const char *name, struct json_object *obj)
551 {
552         struct json_object *p, *i;
553         const char *info;
554
555         info = NULL;
556         if (json_object_object_get_ex(obj, "description", &i))
557                 info = json_object_get_string(i);
558
559         p = permissions_of_verb(obj);
560         printf(
561                 "    {\n"
562                 "        .verb = \"%s\",\n"
563                 "        .callback = "
564                 , name
565         );
566         print_verb(name);
567         printf(
568                 ",\n"
569                 "        .auth = %s,\n"
570                 "        .info = %s,\n"
571                 , p && decl_perm(p) ? json_object_get_string(decl_perm(p)) : "NULL"
572                 , info ? make_info(info, 0) : "NULL"
573         );
574         if (version == 3)
575                 printf(
576                         "        .vcbdata = NULL,\n"
577                 );
578         printf(
579                 "        .session = "
580         );
581         print_session(p);
582         if (version == 3)
583                 printf(
584                         ",\n"
585                         "        .glob = 0"
586                 );
587         printf(
588                 "\n"
589                 "    },\n"
590         );
591 }
592
593 void enum_verbs(void (*func)(const char *name, struct json_object *obj))
594 {
595         struct json_object_iterator ji, jn;
596         struct json_object *paths, *obj;
597         const char *name;
598
599         /* search the verbs */
600         paths = search("#/paths");
601         if (!paths)
602                 return;
603
604         /* list the verbs and sort it */
605         ji = json_object_iter_begin(paths);
606         jn = json_object_iter_end(paths);
607         while (!json_object_iter_equal(&ji, &jn)) {
608                 name = json_object_iter_peek_name(&ji);
609                 obj = json_object_iter_peek_value(&ji);
610                 name += (*name == '/');
611                 func(name, obj);
612                 json_object_iter_next(&ji);
613         }
614 }
615
616 void getvarbool(int *var, const char *path, int defval)
617 {
618         struct json_object *o;
619
620         if (*var != 0 && *var != 1) {
621                 o = search(path);
622                 if (o && json_object_is_type(o, json_type_boolean))
623                         *var = json_object_get_boolean(o);
624                 else
625                         *var = !!defval;
626         }
627 }
628
629 void getvar(const char **var, const char *path, const char *defval)
630 {
631         struct json_object *o;
632
633         if (!*var) {
634                 o = search(path);
635                 if (o && json_object_is_type(o, json_type_string))
636                         *var = json_object_get_string(o);
637                 else
638                         *var = defval;
639         }
640 }
641
642 /**
643  * process a file and prints its expansion on stdout
644  */
645 void process(char *filename)
646 {
647         char *desc;
648         const char *info;
649
650         /* translate - */
651         if (!strcmp(filename, "-"))
652                 filename = "/dev/stdin";
653
654         /* check access */
655         if (access(filename, R_OK)) {
656                 fprintf(stderr, "can't access file %s\n", filename);
657                 exit(1);
658         }
659
660         /* read the file */
661         root = json_object_from_file(filename);
662         if (!root) {
663                 fprintf(stderr, "reading file %s produced null\n", filename);
664                 exit(1);
665         }
666
667         /* create the description */
668         desc = make_desc(root);
669
670         /* expand references */
671         root = expand_$ref((struct path){ .object = root, .upper = NULL });
672
673         /* get some names */
674         getvar(&api, "#/info/x-binding-c-generator/api", NULL);
675         getvar(&preinit, "#/info/x-binding-c-generator/preinit", NULL);
676         getvar(&init, "#/info/x-binding-c-generator/init", NULL);
677         getvar(&onevent, "#/info/x-binding-c-generator/onevent", NULL);
678         getvar(&scope, "#/info/x-binding-c-generator/scope", "static");
679         getvar(&prefix, "#/info/x-binding-c-generator/prefix", "afb_verb_");
680         getvar(&postfix, "#/info/x-binding-c-generator/postfix", "_cb");
681         getvar(&provideclass, "#/info/x-binding-c-generator/provide-class", NULL);
682         getvar(&requireclass, "#/info/x-binding-c-generator/require-class", NULL);
683         getvar(&requireapi, "#/info/x-binding-c-generator/require-api", NULL);
684         getvarbool(&priv, "#/info/x-binding-c-generator/private", 0);
685         getvarbool(&noconc, "#/info/x-binding-c-generator/noconcurrency", 0);
686         getvar(&api, "#/info/title", "?");
687         info = NULL;
688         getvar(&info, "#/info/description", NULL);
689         capi = cify(api);
690
691         /* get the API name */
692         printf(
693                 "\n"
694                 "static const char _afb_description_%s[] =\n"
695                 "%s"
696                 ";\n"
697                 "\n"
698                 , capi, desc
699         );
700         enum_verbs(declare_permissions);
701         print_perms();
702         enum_verbs(print_declare_verb);
703         printf(
704                 "\n"
705                 "static const struct afb_verb_v%d _afb_verbs_%s[] = {\n"
706                 , version, capi
707         );
708         enum_verbs(print_struct_verb);
709         printf(
710                 "    {\n"
711                 "        .verb = NULL,\n"
712                 "        .callback = NULL,\n"
713                 "        .auth = NULL,\n"
714                 "        .info = NULL,\n"
715         );
716         if (version == 3)
717                 printf(
718                         "        .vcbdata = NULL,\n"
719                 );
720         printf(
721                 "        .session = 0"
722         );
723         if (version == 3)
724                 printf(
725                         ",\n"
726                         "        .glob = 0"
727                 );
728         printf(
729                 "\n"
730                 "       }\n"
731                 "};\n"
732         );
733
734         if (T(preinit) || T(init) || T(onevent)) {
735                 printf("\n");
736                 if (T(preinit)) {
737                         if (T(scope)) printf("%s ", scope);
738                         printf("int %s(%s);\n", preinit, version==3 ? "afb_api_t api" : "");
739                 }
740                 if (T(init)) {
741                         if (T(scope)) printf("%s ", scope);
742                         printf("int %s(%s);\n", init, version==3 ? "afb_api_t api" : "");
743                 }
744                 if (T(onevent)) {
745                         if (T(scope)) printf("%s ", scope);
746                         printf("void %s(%sconst char *event, struct json_object *object);\n",
747                                         onevent, version==3 ? "afb_api_t api, " : "");
748                 }
749         }
750
751         printf(
752                 "\n"
753                 "%sconst struct afb_binding_v%d %s%s = {\n"
754                 "    .api = \"%s\",\n"
755                 "    .specification = _afb_description_%s,\n"
756                 "    .info = %s,\n"
757                 "    .verbs = _afb_verbs_%s,\n"
758                 "    .preinit = %s,\n"
759                 "    .init = %s,\n"
760                 "    .onevent = %s,\n"
761                 , priv ? "static " : ""
762                 , version
763                 , priv ? "_afb_binding_" : version==3 ? "afbBindingV3" : "afbBindingV2"
764                 , priv ? capi : ""
765                 , api
766                 , capi
767                 , info ? make_info(info, 0) : "NULL"
768                 , capi
769                 , T(preinit) ? preinit : "NULL"
770                 , T(init) ? init : "NULL"
771                 , T(onevent) ? onevent : "NULL"
772         );
773
774
775         if (version == 3)
776                 printf(
777                         "    .userdata = NULL,\n"
778                         "    .provide_class = %s%s%s,\n"
779                         "    .require_class = %s%s%s,\n"
780                         "    .require_api = %s%s%s,\n"
781                         , T(provideclass) ? "\"" : "", T(provideclass) ? provideclass : "NULL", T(provideclass) ? "\"" : ""
782                         , T(requireclass) ? "\"" : "", T(requireclass) ? requireclass : "NULL", T(requireclass) ? "\"" : ""
783                         , T(requireapi) ? "\"" : "", T(requireapi) ? requireapi : "NULL", T(requireapi) ? "\"" : ""
784                 );
785
786
787         printf(
788                 "    .noconcurrency = %d\n"
789                 "};\n"
790                 "\n"
791                 , !!noconc
792         );
793
794         /* clean up */
795         json_object_put(root);
796         free(desc);
797 }
798
799 /** process the list of files or stdin if none */
800 int main(int ac, char **av)
801 {
802         int r, w;
803         av++;
804
805         r = w = 0;
806         while (av[r]) {
807                 if (!(strcmp(av[r], "-x") && strcmp(av[r], "--cpp"))) {
808                         cpp = 1;
809                         r++;
810                 } else if (!strcmp(av[r], "-2")) {
811                         version = 2;
812                         r++;
813                 } else if (!strcmp(av[r], "-3")) {
814                         version = 3;
815                         r++;
816                 } else {
817                         av[w++] = av[r++];
818                 }
819         }
820         av[w] = NULL;
821         if (!*av)
822                 process("-");
823         else {
824                 do { process(*av++); } while(*av);
825         }
826         return 0;
827 }
828