1 
2 /* makecsd utility - written by Istvan Varga, Mar 2003 */
3 /* updated jpff Feb 2011 */
4 
5 #define VERSION "2.0"
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <errno.h>
11 #include <time.h>
12 #include <ctype.h>
13 #define MAXLICENCE (7)
14 
15 static  char    *orcname = NULL, *sconame = NULL, *optname = NULL;
16 static  char    *midiname = NULL, *scorebin = NULL, *licence = NULL;
17 static  int     licencetype = -1;
18 
19 static  int     nr_infiles = 0;
20 static  int     max_infiles = 0;
21 static  char    **infile_names = NULL;
22 
23 static  int     output_is_stdout = 1;
24 static  char    *outflname = NULL;
25 static  FILE    *outfile = NULL;
26 
27 static  int     tabstop_size = 0;
28 
29 /* convert 6 bits of input and write to output file */
30 extern  int     encode_byte(FILE*, FILE*);
31 /* convert an entire input file */
32 extern  void    encode_file(char*, FILE*, int);
33 /* line width */
34 extern  int     maxlinepos;
35 
setorchnam(char * s)36 static int setorchnam(char *s)
37 {
38     char  *c;
39     if (orcname != NULL) return 0;      /* orchestra name is already set */
40     if (strlen(s) < 5) return 0;        /* name is too short */
41     c = s + (int) strlen(s);
42     /* check if extension is correct */
43     --c; if (*c != 'C' && *c != 'c') return 0;
44     --c; if (*c != 'R' && *c != 'r') return 0;
45     --c; if (*c != 'O' && *c != 'o') return 0;
46     --c; if (*c != '.') return 0;
47     /* set orchestra name and report success */
48     orcname = s;
49     return 1;
50 }
51 
setscornam(char * s)52 static int setscornam(char *s)
53 {
54     char  *c;
55     if (sconame != NULL) return 0;      /* score name is already set */
56     if (strlen(s) < 5) return 0;        /* name is too short */
57     c = s + (int) strlen(s);
58     /* check if extension is correct */
59     --c; if (*c != 'O' && *c != 'o') return 0;
60     --c; if (*c != 'C' && *c != 'c') return 0;
61     --c; if (*c != 'S' && *c != 's') return 0;
62     --c; if (*c != '.') return 0;
63     /* set score name and report success */
64     sconame = s;
65     return 1;
66 }
67 
68 /* read a single character from orc/sco input file, with check for */
69 /* end of file and Unix/MS-DOS/Mac format files */
70 
mygetc(FILE * f,int * end_of_file)71 static int mygetc(FILE *f, int *end_of_file)
72 {
73     int c;
74 
75     if (*end_of_file) return (-1);      /* end of file */
76     c = getc(f);
77     if (c == '\r') {            /* deal with MS-DOS and Mac files */
78       c = getc(f);
79       if (c == EOF) {
80         *end_of_file = 1;
81         return '\n';
82       }
83       if (c != '\n')            /* CR-only: Mac format */
84         ungetc(c, f);
85       return '\n';
86     }
87     if (c == EOF) {
88       *end_of_file = 1;                 /* end of file */
89       return (-1);
90     }
91     return c;
92 }
93 
94 /* write a line to output file, with tab->space conversion if requested */
95 
write_line(FILE * outfl,char * buf,int n)96 static void write_line(FILE *outfl, char *buf, int n)
97 {
98     int linepos = 0;
99     while (n--) {
100       if (tabstop_size && *buf == '\t') {       /* expand tabs to spaces */
101         do {
102           putc(' ', outfl);
103         } while ((++linepos) % tabstop_size);
104       }
105       else {
106         if (*buf == '\n') linepos = -1;     /* new line: reset line position */
107         putc((int) *buf, outfl);
108         linepos++;
109       }
110       buf++;
111     }
112 }
113 
convert_txt_file(char * inflname,FILE * outfl)114 static void convert_txt_file(char *inflname, FILE *outfl)
115 {
116     FILE  *infile;
117     char  linebuf[16384];
118     int   linepos = 0, c, frstline = 1;
119     int   is_eof;
120 
121     /* open file */
122     infile = fopen(inflname, "rb");
123     if (infile == NULL) {
124       fprintf(stderr, "makecsd: error opening input file %s: %s\n",
125                       inflname, strerror(errno));
126       exit(-1);
127     }
128     is_eof = 0;
129     /* read entire file and copy to output */
130     while (!is_eof) {
131       /* read next non-empty line to buffer */
132       linepos = 0;
133       do {
134         c = mygetc(infile, &is_eof);
135         if (is_eof || c == '\n') {
136           /* end of line: remove any trailing white space */
137           while (linepos && isblank(linebuf[linepos - 1]))
138             linepos--;
139           linebuf[linepos++] = '\n';
140           if (is_eof ||                 /* end of file */
141               (linepos > 1 && linebuf[linepos - 2] != '\n'))
142             break;                      /* or a new non-empty line */
143         }
144         else
145           linebuf[linepos++] = c;
146       } while (1);
147       if (is_eof) break;                /* end of file */
148       /* at beginning of file: skip any leading empty lines */
149       if (frstline) {
150         int n = -1;
151         frstline = 0;
152         while (++n < linepos && linebuf[n] == '\n');
153         /* if there is anything to print */
154         if (n < linepos)
155           write_line(outfl, &(linebuf[n]), linepos - n);
156       }
157       else  /* otherwise just print as it is */
158         write_line(outfl, &(linebuf[0]), linepos);
159     }
160     /* at end of file: skip any trailing empty lines */
161     while (linepos && linebuf[linepos - 1] == '\n')
162       linepos--;
163     linebuf[linepos++] = '\n';
164     /* and print last line */
165     write_line(outfl, &(linebuf[0]), linepos);
166     /* close input file */
167     fclose(infile); infile = NULL;
168 }
169 
main(int argc,char ** argv)170 int main(int argc, char **argv)
171 {
172     int i, j;
173 
174     /* parse command line options */
175     i = 0;
176     j = argc;
177     while (--j) {
178       i++;
179       if (!strcmp(argv[i], "-w")) {             /* line width */
180         if (!(--j)) {
181           fprintf(stderr, "makecsd: missing option for -w\n");
182           exit(-1);
183         }
184         i++;
185         maxlinepos = (int) atoi(argv[i]);
186         if (maxlinepos < 20) maxlinepos = 20;
187         if (maxlinepos > 200) maxlinepos = 200;
188       }
189       else if (!strcmp(argv[i], "-o")) {        /* output file name */
190         if (!(--j)) {
191           fprintf(stderr, "makecsd: missing option for -o\n");
192           exit(-1);
193         }
194         i++;
195         outflname = argv[i];
196         if (!strcmp(outflname, "-"))
197           output_is_stdout = 1;
198         else
199           output_is_stdout = 0;
200       }
201       else if (!strcmp(argv[i], "-t")) {        /* tabstop size */
202         if (!(--j)) {
203           fprintf(stderr, "makecsd: missing option for -t\n");
204           exit(-1);
205         }
206         i++;
207         tabstop_size = (int) atoi(argv[i]);
208         if (tabstop_size < 0) tabstop_size = 0;
209         if (tabstop_size > 24) tabstop_size = 24;
210       }
211       else if (!strcmp(argv[i], "-f")) {        /* options file name */
212         if (!(--j)) {
213           fprintf(stderr, "makecsd: missing option for -f\n");
214           exit(-1);
215         }
216         i++;
217         optname = argv[i];
218       }
219       else if (!strcmp(argv[i], "-m")) {        /* options file name */
220         if (!(--j)) {
221           fprintf(stderr, "makecsd: missing option for -m\n");
222           exit(-1);
223         }
224         i++;
225         midiname = argv[i];
226       }
227       else if (!strcmp(argv[i], "-b")) {        /* options file name */
228         if (!(--j)) {
229           fprintf(stderr, "makecsd: missing option for -b\n");
230           exit(-1);
231         }
232         i++;
233         scorebin = argv[i];
234       }
235       else if (!strcmp(argv[i], "-L")) {        /* options file name */
236         if (!(--j)) {
237           fprintf(stderr, "makecsd: missing option for -L\n");
238           exit(-1);
239         }
240         i++;
241         licence = argv[i];
242         licencetype = 99;
243       }
244       else if (!strcmp(argv[i], "-l")) {        /* options file name */
245         if (!(--j)) {
246           fprintf(stderr, "makecsd: missing option for -l\n");
247           exit(-1);
248         }
249         i++;
250         licencetype = atoi(argv[i]);
251         if (licencetype>MAXLICENCE || licencetype<0) licencetype = -1;
252       }
253       else {                                    /* input file name */
254         if (*(argv[i]) == '-') {
255           fprintf(stderr, "makecsd: invalid option: %s\n", argv[i]);
256           exit(-1);
257         }
258         if (!(setorchnam(argv[i]) || setscornam(argv[i]))) {
259           /* if input is neither an orchestra, nor a score file, */
260           /* encode it as Base64 */
261           if (nr_infiles >= max_infiles) {
262             /* extend number of input files */
263             max_infiles = max_infiles + (max_infiles >> 2) + 16;
264             infile_names = (char**) realloc(infile_names,
265                                             sizeof(char*) * max_infiles);
266             if (infile_names == NULL) {
267               fprintf(stderr, "makecsd: not enough memory\n");
268               exit(-1);
269             }
270           }
271           infile_names[nr_infiles] = argv[i];
272           nr_infiles++;
273         }
274       }
275     }
276     if (!nr_infiles && (orcname == NULL && sconame == NULL)) {
277       /* print usage */
278       fprintf(stderr, "makecsd: no input files\n");
279       fprintf(stderr,
280               "usage: makecsd [OPTIONS ... ] infile1 [ infile2 [ ... ]]\n");
281       fprintf(stderr, "options:\n");
282       fprintf(stderr,
283               "    -t <n>      expand tabs to spaces using tabstop size ");
284       fprintf(stderr, "n (default: disabled)\n");
285       fprintf(stderr,
286               "    -w <n>      set Base64 line width to n (default: 72)\n");
287       fprintf(stderr,
288               "    -f <fname>  read CSD options from file 'fname'\n");
289       fprintf(stderr,
290               "    -o <fname>  output file name (default: stdout)\n");
291       /* **** NEW OPTIONS **** */
292       //      fprintf(stderr,
293       //              "    -v[ba] x.y  use version (before/after) x.y\n");
294       fprintf(stderr,
295               "    -l int      licence type (0-1)\n");
296       fprintf(stderr,
297               "    -L fname    licence file\n");
298       fprintf(stderr,
299               "    -b <pname>  program to process score'\n");
300       fprintf(stderr,
301               "    -m <fname>  MIDI file name\n");
302       exit(-1);
303     }
304     /* open output file */
305     if (!output_is_stdout) {
306       outfile = fopen(outflname, "w");
307       if (outfile == NULL) {
308         fprintf(stderr, "makecsd: error opening output file %s: %s\n",
309                         outflname, strerror(errno));
310         exit(-1);
311       }
312     }
313     else
314       outfile = stdout;
315     /* create CSD file */
316     fprintf(outfile, "<CsoundSynthesizer>\n");
317     fprintf(outfile, ";;; this CSD file generated by makecsd v" VERSION "\n");
318     fprintf(outfile, "<CsOptions>\n");
319     fprintf(outfile, ";;; set command line options here\n");
320     /* CSD options */
321     for (i = 0; i < nr_infiles; i++) {
322       char *s = infile_names[i], *s0;
323       j = (int) strlen(s);
324       if (j < 4) continue;
325       /* check for MIDI file */
326       if (s[j - 1] != 'D' && s[j - 1] != 'd') continue;
327       if (s[j - 2] != 'I' && s[j - 2] != 'i') continue;
328       if (s[j - 3] != 'M' && s[j - 3] != 'm') continue;
329       if (s[j - 4] != '.') continue;
330       /* add to options if found */
331       /* strip any leading components from file name */
332       s0 = s; s = s0 + ((int) strlen(s0) - 1);
333       while (s >= s0 && *s != '/' && *s != '\\' && *s != ':') s--;
334       s++;
335       fprintf(outfile, "-F %s\n", s);
336       break;
337     }
338     if (optname != NULL) {
339       /* copy options file if specified */
340       convert_txt_file(optname, outfile);
341       /* hack: remove blank line from end of options */
342       fflush(outfile);
343       if (fseek(outfile, -1L, SEEK_END)<0) {
344         fprintf(stderr, "makecsd: seek failed\n");
345         exit(1);
346       }
347     }
348     else if (i >= nr_infiles) {
349       fprintf(outfile, "\n");   /* put blank line if there are no options */
350     }
351     fprintf(outfile, "</CsOptions>\n");
352     if (licencetype == 99) {    /* Licence file */
353       fprintf(outfile, "<CsLicence>\n");
354       convert_txt_file(licence, outfile);
355       fprintf(outfile, "</CsLicence>\n");
356     }
357     else if (licencetype < MAXLICENCE) { /* precanned licence */
358       char buff[32];
359       time_t tt = time(NULL);
360       strftime(buff, 32, "<CsLicence>\nCopyright %Y: ", gmtime(&tt));
361       fprintf(outfile, "%s", buff);
362       switch (licencetype) {
363       default:
364       case 0:
365         fprintf(outfile, "All Rights Reserved\n");
366         break;
367       case 1:
368         fprintf(outfile, "Creative Commons "
369                 "Attribution-NonCommercial-NoDerivatives\n"
370                 "CC BY-NC-ND\n");
371         break;
372       case 2:
373         fprintf(outfile, "Creative Commons "
374                 "Attribution-NonCommercial-ShareAlike\n"
375                 "CC BY-NC-SA\n");
376         break;
377       case 3:
378         fprintf(outfile, "Creative Commons "
379                 "Attribution-NonCommercial\n"
380                 "CC BY-NC\n");
381          break;
382      case 4:
383         fprintf(outfile, "Creative Commons "
384                 "Attribution-NoDerivatives\n"
385                 "CC BY-ND\n");
386         break;
387       case 5:
388         fprintf(outfile, "Creative Commons "
389                 "Attribution-ShareAlike\n"
390                 "CC BY-SA\n");
391         break;
392       case 6:
393         fprintf(outfile, "Creative Commons "
394                 "Attribution\n"
395                 "CC BY\n");
396         break;
397       case 7:
398         fprintf(outfile, "Licenced under BSD\n");
399         break;
400       }
401       fprintf(outfile, "</CsLicence>\n");
402     }
403     /* orchestra */
404     if (orcname != NULL) {
405       fprintf(outfile, "<CsInstruments>\n");
406       convert_txt_file(orcname, outfile);
407       fprintf(outfile, "</CsInstruments>\n");
408     }
409     else {
410       fprintf(stderr, "makecsd: warning: no orchestra file\n");
411     }
412     /* score */
413     if (sconame != NULL) {
414       if (scorebin)
415         fprintf(outfile, "<CsScore bin=\"%s\">\n", scorebin);
416       else
417         fprintf(outfile, "<CsScore>\n");
418       convert_txt_file(sconame, outfile);
419       fprintf(outfile, "</CsScore>\n");
420     }
421     else {
422       fprintf(stderr, "makecsd: warning: no score file\n");
423     }
424     if (midiname != NULL) {
425       fprintf(outfile, "<CsMidifileB>\n");
426       encode_file(midiname, outfile, 1);
427       fprintf(outfile, "</CsMidifileB>\n");
428     }
429     /* Base64 encode any remaining input files */
430     for (i = 0; i < nr_infiles; i++)
431       encode_file(infile_names[i], outfile, 0);
432     /* end of CSD file */
433     fprintf(outfile, "</CsoundSynthesizer>\n\n");
434     /* close output file */
435     if (!output_is_stdout) {
436       fflush(outfile);
437       fclose(outfile);
438     }
439     free(infile_names);
440     return 0;
441 }
442 
443