Addes jpeg2000 support to cmake.
[blender-staging.git] / source / blender / python / BPY_menus.c
1 /* 
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * This is a new part of Blender.
24  *
25  * Contributor(s): Willian P. Germano, Michael Reimpell
26  *
27  * ***** END GPL LICENSE BLOCK *****
28 */
29
30 /* 
31  *This is the main file responsible for having bpython scripts accessible
32  * from Blender menus.  To know more, please start with its header file.
33  */
34
35 #include "BPY_menus.h"
36
37 #include <Python.h>
38 #ifndef WIN32
39   #include <dirent.h>
40 #else
41   #include "BLI_winstuff.h"
42 #endif
43 #include "BKE_global.h"
44 #include "BKE_utildefines.h"
45 #include "BIF_keyval.h"
46 #include "BLI_blenlib.h"
47 #include "MEM_guardedalloc.h"
48 #include "DNA_userdef_types.h"  /* for U.pythondir */
49 #include "api2_2x/EXPP_interface.h" /* for bpy_gethome() */
50
51 #define BPYMENU_DATAFILE "Bpymenus"
52 #define MAX_DIR_DEPTH 4 /* max depth for traversing scripts dirs */
53 #define MAX_DIR_NUMBER 30 /* max number of dirs in scripts dirs trees */
54
55 static int DEBUG;
56 static int Dir_Depth;
57 static int Dirs_Number;
58
59 /* BPyMenuTable holds all registered pymenus, as linked lists for each menu
60  * where they can appear (see PYMENUHOOKS enum in BPY_menus.h).
61 */
62 BPyMenu *BPyMenuTable[PYMENU_TOTAL];
63
64 static int bpymenu_group_atoi( char *str )
65 {
66         if( !strcmp( str, "Export" ) )
67                 return PYMENU_EXPORT;
68         else if( !strcmp( str, "Import" ) )
69                 return PYMENU_IMPORT;
70         else if( !strcmp( str, "Help" ) )
71                 return PYMENU_HELP;
72         else if( !strcmp( str, "HelpWebsites" ) )
73                 return PYMENU_HELPWEBSITES;
74         else if( !strcmp( str, "HelpSystem" ) )
75                 return PYMENU_HELPSYSTEM;
76         else if( !strcmp( str, "Render" ) )
77                 return PYMENU_RENDER;
78         else if( !strcmp( str, "System" ) )
79                 return PYMENU_SYSTEM;
80         else if( !strcmp( str, "Object" ) )
81                 return PYMENU_OBJECT;
82         else if( !strcmp( str, "Mesh" ) )
83                 return PYMENU_MESH;
84         else if( !strncmp( str, "Theme", 5 ) )
85                 return PYMENU_THEMES;
86         else if( !strcmp( str, "Add" ) )
87                 return PYMENU_ADD;
88         else if( !strcmp( str, "Wizards" ) )
89                 return PYMENU_WIZARDS;
90         else if( !strcmp( str, "Animation" ) )
91                 return PYMENU_ANIMATION;
92         else if( !strcmp( str, "Materials" ) )
93                 return PYMENU_MATERIALS;
94         else if( !strcmp( str, "UV" ) )
95                 return PYMENU_UV;
96         else if( !strcmp( str, "Image" ) )
97                 return PYMENU_IMAGE;
98         else if( !strcmp( str, "FaceSelect" ) )
99                 return PYMENU_FACESELECT;
100         else if( !strcmp( str, "WeightPaint" ) )
101                 return PYMENU_WEIGHTPAINT;
102         else if( !strcmp( str, "VertexPaint" ) )
103                 return PYMENU_VERTEXPAINT;
104         else if( !strcmp( str, "UVCalculation" ) )
105                 return PYMENU_UVCALCULATION;
106         else if( !strcmp( str, "Armature" ) )
107                 return PYMENU_ARMATURE;
108         else if( !strcmp( str, "ScriptTemplate" ) )
109                 return PYMENU_SCRIPTTEMPLATE;
110         else if( !strcmp( str, "TextPlugin" ) )
111                 return PYMENU_TEXTPLUGIN;
112         else if( !strcmp( str, "MeshFaceKey" ) )
113                 return PYMENU_MESHFACEKEY;
114         else if( !strcmp( str, "AddMesh" ) )
115                 return PYMENU_ADDMESH;
116         /* "Misc" or an inexistent group name: use misc */
117         else
118                 return PYMENU_MISC;
119 }
120
121 char *BPyMenu_group_itoa( short menugroup )
122 {
123         switch ( menugroup ) {
124         case PYMENU_EXPORT:
125                 return "Export";
126                 break;
127         case PYMENU_IMPORT:
128                 return "Import";
129                 break;
130         case PYMENU_ADD:
131                 return "Add";
132                 break;
133         case PYMENU_HELP:
134                 return "Help";
135                 break;
136         case PYMENU_HELPWEBSITES:
137                 return "HelpWebsites";
138                 break;
139         case PYMENU_HELPSYSTEM:
140                 return "HelpSystem";
141                 break;
142         case PYMENU_RENDER:
143                 return "Render";
144                 break;
145         case PYMENU_SYSTEM:
146                 return "System";
147                 break;
148         case PYMENU_OBJECT:
149                 return "Object";
150                 break;
151         case PYMENU_MESH:
152                 return "Mesh";
153                 break;
154         case PYMENU_THEMES:
155                 return "Themes";
156                 break;
157         case PYMENU_WIZARDS:
158                 return "Wizards";
159                 break;
160         case PYMENU_ANIMATION:
161                 return "Animation";
162                 break;
163         case PYMENU_MATERIALS:
164                 return "Materials";
165                 break;
166         case PYMENU_UV:
167                 return "UV";
168                 break;
169         case PYMENU_IMAGE:
170                 return "Image";
171                 break;
172         case PYMENU_FACESELECT:
173                 return "FaceSelect";
174                 break;
175         case PYMENU_WEIGHTPAINT:
176                 return "WeightPaint";
177                 break;
178         case PYMENU_VERTEXPAINT:
179                 return "VertexPaint";
180                 break;
181         case PYMENU_UVCALCULATION:
182                 return "UVCalculation";
183                 break;
184         case PYMENU_ARMATURE:
185                 return "Armature";
186                 break;
187         case PYMENU_SCRIPTTEMPLATE:
188                 return "ScriptTemplate";
189                 break;
190         case PYMENU_TEXTPLUGIN:
191                 return "TextPlugin";
192                 break;
193         case PYMENU_MESHFACEKEY:
194                 return "MeshFaceKey";
195                 break;
196         case PYMENU_ADDMESH:
197                 return "AddMesh";
198                 break;
199         case PYMENU_MISC:
200                 return "Misc";
201                 break;
202         }
203         return NULL;
204 }
205
206 /* BPyMenu_CreatePupmenuStr:
207  * build and return a meaninful string to be used by pupmenu().  The
208  * string is made of a bpymenu name as title and its submenus as possible
209  * choices for the user.
210 */
211 char *BPyMenu_CreatePupmenuStr( BPyMenu * pym, short menugroup )
212 {
213         BPySubMenu *pysm = pym->submenus;
214         char str[1024], str2[100];
215         int i = 0, rlen;
216
217         if( !pym || !pysm )
218                 return NULL;
219
220         str[0] = '\0';
221
222         PyOS_snprintf( str2, sizeof( str2 ), "%s: %s%%t",
223                        BPyMenu_group_itoa( menugroup ), pym->name );
224         strcat( str, str2 );
225
226         while( pysm ) {
227                 PyOS_snprintf( str2, sizeof( str2 ), "|%s%%x%d", pysm->name,
228                                i );
229                 rlen = sizeof( str ) - strlen( str );
230                 strncat( str, str2, rlen );
231                 i++;
232                 pysm = pysm->next;
233         }
234
235         return BLI_strdup( str );
236 }
237
238 static void bpymenu_RemoveAllSubEntries( BPySubMenu * smenu )
239 {
240         BPySubMenu *tmp;
241
242         while( smenu ) {
243                 tmp = smenu->next;
244                 if( smenu->name )
245                         MEM_freeN( smenu->name );
246                 if( smenu->arg )
247                         MEM_freeN( smenu->arg );
248                 MEM_freeN( smenu );
249                 smenu = tmp;
250         }
251         return;
252 }
253
254 void BPyMenu_RemoveAllEntries( void )
255 {
256         BPyMenu *tmp, *pymenu;
257         int i;
258
259         for( i = 0; i < PYMENU_TOTAL; i++ ) {
260                 pymenu = BPyMenuTable[i];
261                 while( pymenu ) {
262                         tmp = pymenu->next;
263                         if( pymenu->name )
264                                 MEM_freeN( pymenu->name );
265                         if( pymenu->filename )
266                                 MEM_freeN( pymenu->filename );
267                         if( pymenu->tooltip )
268                                 MEM_freeN( pymenu->tooltip );
269                         if( pymenu->submenus )
270                                 bpymenu_RemoveAllSubEntries( pymenu->
271                                                              submenus );
272                         MEM_freeN( pymenu );
273                         pymenu = tmp;
274                 }
275                 BPyMenuTable[i] = NULL;
276         }
277
278         Dirs_Number = 0;
279         Dir_Depth = 0;
280
281         return;
282 }
283
284 static BPyMenu *bpymenu_FindEntry( short group, char *name )
285 {
286         BPyMenu *pymenu;
287
288         if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) )
289                 return NULL;
290
291         pymenu = BPyMenuTable[group];
292
293         while( pymenu ) {
294                 if( !strcmp( pymenu->name, name ) )
295                         return pymenu;
296                 pymenu = pymenu->next;
297         }
298
299         return NULL;
300 }
301
302 /* BPyMenu_GetEntry:
303  * given a group and a position, return the entry in that position from
304  * that group.
305 */
306 BPyMenu *BPyMenu_GetEntry( short group, short pos )
307 {
308         BPyMenu *pym = NULL;
309
310         if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) )
311                 return NULL;
312
313         pym = BPyMenuTable[group];
314
315         while( pos-- ) {
316                 if( pym )
317                         pym = pym->next;
318                 else
319                         break;
320         }
321
322         return pym;             /* found entry or NULL */
323 }
324
325 static void bpymenu_set_tooltip( BPyMenu * pymenu, char *tip )
326 {
327         if( !pymenu )
328                 return;
329
330         if( pymenu->tooltip )
331                 MEM_freeN( pymenu->tooltip );
332         pymenu->tooltip = BLI_strdup( tip );
333
334         return;
335 }
336
337 static void bpymenu_set_shortcut( BPyMenu * pymenu, char *combi )
338 {
339         unsigned short key, qual;
340
341         if( !pymenu )
342                 return;
343
344         if (!decode_key_string(combi, &key, &qual)) {
345                 return; /* TODO: Print some error */
346         }
347
348         pymenu->key = key;
349         pymenu->qual = qual;
350
351         return;
352 }
353
354 /* bpymenu_AddEntry:
355  * try to find an existing pymenu entry with the given type and name;
356  * if found, update it with new info, otherwise create a new one and fill it.
357  */
358 static BPyMenu *bpymenu_AddEntry( short group, short version, char *name,
359         char *fname, int is_userdir, char *tooltip )
360 {
361         BPyMenu *menu, *next = NULL, **iter;
362         int nameclash = 0;
363
364         if( ( group < 0 ) || ( group >= PYMENU_TOTAL ) )
365                 return NULL;
366         if( !name || !fname )
367                 return NULL;
368
369         menu = bpymenu_FindEntry( group, name );        /* already exists? */
370
371         /* if a menu with this name already exists in the same group:
372          * - if one script is in the default dir and the other in U.pythondir,
373          *   accept and let the new one override the other.
374          * - otherwise, report the error and return NULL. */
375         if( menu ) {
376                 if( menu->dir < is_userdir ) {  /* new one is in U.pythondir */
377                         nameclash = 1;
378                         if( menu->name )
379                                 MEM_freeN( menu->name );
380                         if( menu->filename )
381                                 MEM_freeN( menu->filename );
382                         if( menu->tooltip )
383                                 MEM_freeN( menu->tooltip );
384                         if( menu->submenus )
385                                 bpymenu_RemoveAllSubEntries( menu->submenus );
386                         next = menu->next;
387                 } else {        /* they are in the same dir */
388                         if (DEBUG) {
389                                 fprintf(stderr, "\n\
390 Warning: script %s's menu name is already in use.\n\
391 Edit the script and change its \n\
392 Name: '%s'\n\
393 field, please.\n\
394 Note: if you really want to have two scripts for the same menu with\n\
395 the same name, keep one in the default dir and the other in\n\
396 the user defined dir (only the later will be registered).\n", fname, name);
397                         }
398                         return NULL;
399                 }
400         } else
401                 menu = MEM_mallocN( sizeof( BPyMenu ), "pymenu" );
402
403         if( !menu )
404                 return NULL;
405
406         menu->name = BLI_strdup( name );
407         menu->version = version;
408         menu->filename = BLI_strdup( fname );
409         menu->tooltip = NULL;
410         if( tooltip )
411                 menu->tooltip = BLI_strdup( tooltip );
412         menu->dir = is_userdir;
413         menu->submenus = NULL;
414         menu->next = next;      /* non-NULL if menu already existed */
415
416         if( nameclash )
417                 return menu;    /* no need to place it, it's already at the list */
418         else {  /* insert the new entry in its correct position at the table */
419                 BPyMenu *prev = NULL;
420                 char *s = NULL;
421
422                 iter = &BPyMenuTable[group];
423                 while( *iter ) {
424                         s = ( *iter )->name;
425                         if( s )
426                                 if( strcmp( menu->name, s ) < 0 )
427                                         break;  /* sort by names */
428                         prev = *iter;
429                         iter = &( ( *iter )->next );
430                 }
431
432                 if( *iter ) {   /* prepend */
433                         menu->next = *iter;
434                         if( prev )
435                                 prev->next = menu;
436                         else
437                                 BPyMenuTable[group] = menu;     /* is first entry */
438                 } else
439                         *iter = menu;   /* append */
440         }
441
442         return menu;
443 }
444
445 /* bpymenu_AddSubEntry:
446  * add a submenu to an existing python menu.
447  */
448 static int bpymenu_AddSubEntry( BPyMenu * mentry, char *name, char *arg )
449 {
450         BPySubMenu *smenu, **iter;
451
452         smenu = MEM_mallocN( sizeof( BPySubMenu ), "pysubmenu" );
453         if( !smenu )
454                 return -1;
455
456         smenu->name = BLI_strdup( name );
457         smenu->arg = BLI_strdup( arg );
458         smenu->next = NULL;
459
460         if( !smenu->name || !smenu->arg )
461                 return -1;
462
463         iter = &( mentry->submenus );
464         while( *iter )
465                 iter = &( ( *iter )->next );
466
467         *iter = smenu;
468
469         return 0;
470 }
471
472 /* bpymenu_CreateFromFile:
473  * parse the bpymenus data file where Python menu data is stored;
474  * based on this data, create and fill the pymenu structs.
475  */
476 static int bpymenu_CreateFromFile( void )
477 {
478         FILE *fp;
479         char line[255], w1[255], w2[255], tooltip[255], *tip;
480         char upythondir[FILE_MAX];
481         char *homedir = NULL;
482         int parsing, version, w2_len, is_userdir;
483         short group;
484         BPyMenu *pymenu = NULL;
485
486         /* init global bpymenu table (it is a list of pointers to struct BPyMenus
487          * for each available cathegory: import, export, etc.) */
488         for( group = 0; group < PYMENU_TOTAL; group++ )
489                 BPyMenuTable[group] = NULL;
490
491         /* let's try to open the file with bpymenu data */
492         homedir = bpy_gethome(0);
493         if (!homedir) {
494                 if( DEBUG )
495                         fprintf(stderr,
496                                 "BPyMenus error: couldn't open config file Bpymenus: no home dir.\n");
497                 return -1;
498         }
499
500         BLI_make_file_string( "/", line, homedir, BPYMENU_DATAFILE );
501
502         fp = fopen( line, "rb" );
503
504         if( !fp ) {
505                 if( DEBUG )
506                         fprintf(stderr, "BPyMenus error: couldn't open config file %s.\n", line );
507                 return -1;
508         }
509
510         fgets( line, 255, fp ); /* header */
511
512         /* check if the U.pythondir we saved at the file is different from the
513          * current one.  If so, return to force updating from dirs */
514         w1[0] = '\0';
515         fscanf( fp, "# User defined scripts dir: %[^\n]\n", w1 );
516
517                 BLI_strncpy(upythondir, U.pythondir, FILE_MAX);
518                 BLI_convertstringcode(upythondir, G.sce);
519
520                 if( strcmp( w1, upythondir ) != 0 )
521                         return -1;
522
523                 w1[0] = '\0';
524
525         while( fgets( line, 255, fp ) ) {       /* parsing file lines */
526
527                 switch ( line[0] ) {    /* check first char */
528                 case '#':       /* comment */
529                         continue;
530                         break;
531                 case '\n':
532                         continue;
533                         break;
534                 default:
535                         parsing = sscanf( line, "%s {\n", w1 ); /* menu group */
536                         break;
537                 }
538
539                 if( parsing == 1 ) {    /* got menu group string */
540                         group = (short)bpymenu_group_atoi( w1 );
541                         if( group < 0 && DEBUG ) {      /* invalid type */
542                                 fprintf(stderr,
543                                         "BPyMenus error parsing config file: wrong group: %s,\n\
544 will use 'Misc'.\n", w1 );
545                         }
546                 } else
547                         continue;
548
549                 for(;;) {
550                         tip = NULL;     /* optional tooltip */
551                         fgets( line, 255, fp );
552                         if( line[0] == '}' )
553                                 break;
554                         else if( line[0] == '\n' )
555                                 continue;
556                         else if( line[0] == '\'' ) {    /* menu entry */
557 /*                              parsing =
558                                         sscanf( line,
559                                                 "'%[^']' %d %s %d '%[^']'\n",
560                                                 w1, &version, w2, &is_userdir,
561                                                 tooltip );
562 */
563                                 /* previously filenames with spaces were not supported;
564                                  * this adds support for that w/o breaking the existing
565                                  * few, exotic scripts that parse the Bpymenus file */
566                                 parsing = sscanf( line,
567                                                 "'%[^']' %d %[^'\n] '%[^']'\n",
568                                                 w1, &version, w2, tooltip );
569                                 if( parsing <= 0 ) { /* invalid line, get rid of it */
570                                         fgets( line, 255, fp );
571                                 } else if( parsing == 4 )
572                                         tip = tooltip;  /* has tooltip */
573
574                                 w2_len = strlen(w2);
575                                 if( w2[w2_len-1] == ' ') {
576                                         w2[w2_len-1] = '\0';
577                                         w2_len -= 1;
578                                 }
579                                 if( w2[w2_len-1] == '1') is_userdir = 1;
580                                 else is_userdir = 0;
581                                 w2[w2_len-2] = '\0';
582
583                                 pymenu = bpymenu_AddEntry( group,
584                                                            ( short ) version,
585                                                            w1, w2, is_userdir,
586                                                            tip );
587                                 if( !pymenu ) {
588                                         puts( "BPyMenus error: couldn't create bpymenu entry.\n" );
589                                         fclose( fp );
590                                         return -1;
591                                 }
592                         } else if( line[0] == '|' && line[1] == '_' ) { /* menu sub-entry */
593                                 if( !pymenu )
594                                         continue;       /* no menu yet, skip this line */
595                                 sscanf( line, "|_%[^:]: %s\n", w1, w2 );
596                                 bpymenu_AddSubEntry( pymenu, w1, w2 );
597                         }
598                 }
599         }
600
601         fclose( fp );
602         return 0;
603 }
604
605 /* bpymenu_WriteDataFile:
606  * writes the registered scripts info to the user's home dir, for faster
607  * access when the scripts dir hasn't changed.
608 */
609 static void bpymenu_WriteDataFile( void )
610 {
611         BPyMenu *pymenu;
612         BPySubMenu *smenu;
613         FILE *fp;
614         char fname[FILE_MAXDIR], *homedir;
615         int i;
616
617         homedir = bpy_gethome(0);
618
619         if (!homedir) {
620                 if( DEBUG )
621                         fprintf(stderr,
622                                 "BPyMenus error: couldn't write Bpymenus file: no home dir.\n\n");
623                 return;
624         }
625
626         BLI_make_file_string( "/", fname, homedir, BPYMENU_DATAFILE );
627
628         fp = fopen( fname, "w" );
629         if( !fp ) {
630                 if( DEBUG )
631                         fprintf(stderr, "BPyMenus error: couldn't write %s file.\n\n",
632                                 fname );
633                 return;
634         }
635
636         fprintf( fp,
637                  "# Blender: registered menu entries for bpython scripts\n" );
638
639         if (U.pythondir[0] != '\0' &&
640                         strcmp(U.pythondir, "/") != 0 && strcmp(U.pythondir, "//") != 0)
641         {
642                 char upythondir[FILE_MAX];
643
644                 BLI_strncpy(upythondir, U.pythondir, FILE_MAX);
645                 BLI_convertstringcode(upythondir, G.sce);
646                 fprintf( fp, "# User defined scripts dir: %s\n", upythondir );
647         }
648
649         for( i = 0; i < PYMENU_TOTAL; i++ ) {
650                 pymenu = BPyMenuTable[i];
651                 if( !pymenu )
652                         continue;
653                 fprintf( fp, "\n%s {\n", BPyMenu_group_itoa( (short)i ) );
654                 while( pymenu ) {
655                         fprintf( fp, "'%s' %d %s %d", pymenu->name,
656                                  pymenu->version, pymenu->filename,
657                                  pymenu->dir );
658                         if( pymenu->tooltip )
659                                 fprintf( fp, " '%s'\n", pymenu->tooltip );
660                         else
661                                 fprintf( fp, "\n" );
662                         smenu = pymenu->submenus;
663                         while( smenu ) {
664                                 fprintf( fp, "|_%s: %s\n", smenu->name,
665                                          smenu->arg );
666                                 smenu = smenu->next;
667                         }
668                         pymenu = pymenu->next;
669                 }
670                 fprintf( fp, "}\n" );
671         }
672
673         fclose( fp );
674         return;
675 }
676
677 /* BPyMenu_PrintAllEntries:
678  * useful for debugging.
679  */
680 void BPyMenu_PrintAllEntries( void )
681 {
682         BPyMenu *pymenu;
683         BPySubMenu *smenu;
684         int i;
685
686         printf( "# Blender: registered menu entries for bpython scripts\n" );
687
688         for( i = 0; i < PYMENU_TOTAL; i++ ) {
689                 pymenu = BPyMenuTable[i];
690                 printf( "\n%s {\n", BPyMenu_group_itoa( (short)i ) );
691                 while( pymenu ) {
692                         printf( "'%s' %d %s %d", pymenu->name, pymenu->version,
693                                 pymenu->filename, pymenu->dir );
694                         if( pymenu->tooltip )
695                                 printf( " '%s'\n", pymenu->tooltip );
696                         else
697                                 printf( "\n" );
698                         smenu = pymenu->submenus;
699                         while( smenu ) {
700                                 printf( "|_%s: %s\n", smenu->name,
701                                         smenu->arg );
702                                 smenu = smenu->next;
703                         }
704                         pymenu = pymenu->next;
705                 }
706                 printf( "}\n" );
707         }
708 }
709
710 /* bpymenu_ParseFile:
711  * parse a given .py file looking for a proper header.
712  *
713  * The first line of the script must be '#!BPY'.
714  * The header registration lines must appear between the first pair of
715  * '\"\"\"' and follow this order (the single-quotes are part of
716  * the format):
717  *
718  * # \"\"\"<br>
719  * # Name: 'script name for the menu'
720  * # Blender: <code>short int</code> (minimal Blender version)
721  * # Group: 'group name' (defines menu)
722  * # Submenu: 'submenu name' related_1word_arg
723  * # Shortcut: Modifier+Key (optional shortcut combination for supported groups)
724  * # Tooltip: 'tooltip for the menu'
725  * # \"\"\"
726  *
727  * Notes:
728  *
729  * - Commenting out header lines with "#" is optional, but recommended.
730  * - There may be more than one submenu line, or none:
731  *   submenus and the tooltip are optional;
732  * - The Blender version is the same number reported by 
733  *   Blender.Get('version') in BPython or G.version in C;
734  * - Line length must be less than 99.
735  */
736 static int bpymenu_ParseFile(FILE *file, char *fname, int is_userdir)
737 {
738         char line[100];
739         char head[100];
740         char middle[100];
741         char tail[100];
742         int matches;
743         int parser_state;
744
745         char script_name[100];
746         int script_version = 1;
747         int script_group;
748
749         BPyMenu *scriptMenu = NULL;
750
751         if (file != NULL) {
752                 parser_state = 1; /* state of parser, 0 to terminate */
753
754                 while ((parser_state != 0) && (fgets(line, 100, file) != NULL)) {
755
756                         switch (parser_state) {
757
758                                 case 1: /* !BPY */
759                                         if (strncmp(line, "#!BPY", 5) == 0) {
760                                                 parser_state++;
761                                         } else {
762                                                 parser_state = 0;
763                                         }
764                                         break;
765
766                                 case 2: /* \"\"\" */
767                                         if ((strstr(line, "\"\"\""))) {
768                                                 parser_state++;
769                                         }
770                                         break;
771
772                                 case 3: /* Name: 'script name for the menu' */
773                                         matches = sscanf(line, "%[^']'%[^']'%c", head, script_name, tail);
774                                         if ((matches == 3) && (strstr(head, "Name:") != NULL)) {
775                                                 parser_state++;
776                                         } else {
777                                                 if (DEBUG)
778                                                         fprintf(stderr, "BPyMenus error: Wrong 'Name' line: %s\n", fname);
779                                                 parser_state = 0;
780                                         }
781                                         break;
782
783                                 case 4: /* Blender: <short int> */
784                                         matches = sscanf(line, "%[^1234567890]%i%c", head, &script_version,
785                                                 tail);
786                                         if (matches == 3) {
787                                                 parser_state++;
788                                         } else {
789                                                 if (DEBUG)
790                                                         fprintf(stderr,"BPyMenus error: Wrong 'Blender' line: %s\n",fname);
791                                                 parser_state = 0;
792                                         }
793                                         break;
794
795                                 case 5: /* Group: 'group name' */
796                                         matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail);
797                                         if ((matches == 3) && (strstr(head, "Group:") != NULL)) {
798                                                 script_group = bpymenu_group_atoi(middle);
799                                                 if (script_group < 0) {
800                                                         if (DEBUG)
801                                                                 fprintf(stderr, "BPyMenus error: Unknown group \"%s\": %s\n",
802                                                                         middle, fname);
803                                                         parser_state = 0;
804                                                 }
805                                                 
806                                                 else { /* register script */
807                                                         scriptMenu = bpymenu_AddEntry((short)script_group,
808                                                                 (short int)script_version, script_name, fname, is_userdir,NULL);
809                                                         if (scriptMenu == NULL) {
810                                                                 if (DEBUG)
811                                                                         fprintf(stderr,
812                                                                                 "BPyMenus error: Couldn't create entry for: %s\n", fname);
813                                                                 parser_state = 0;
814                                                         } else {
815                                                                 parser_state++;
816                                                         }
817                                                 }
818
819                                         } else {
820                                                 if (DEBUG)
821                                                         fprintf(stderr, "BPyMenus error: Wrong 'Group' line: %s\n",fname);
822                                                 parser_state = 0;
823                                         }
824                                         break;
825
826                                 case 6: /* optional elements */
827                                         /* Submenu: 'submenu name' related_1word_arg */
828                                         matches = sscanf(line, "%[^']'%[^']'%s\n", head, middle, tail);
829                                         if ((matches == 3) && (strstr(head, "Submenu:") != NULL)) {
830                                                 bpymenu_AddSubEntry(scriptMenu, middle, tail);
831                                         } else {
832                                                 /* Shortcut: 'key+combination' */
833                                                 matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail);
834                                                 if ((matches == 3) && (strstr(head, "Shortcut:") != NULL)) {
835                                                         bpymenu_set_shortcut(scriptMenu, middle);
836                                                 } else {
837                                                         /* Tooltip: 'tooltip for the menu */
838                                                         matches = sscanf(line, "%[^']'%[^']'%c", head, middle, tail);
839                                                         if ((matches == 3) && ((strstr(head, "Tooltip:") != NULL) ||
840                                                                 (strstr(head, "Tip:") != NULL))) {
841                                                                 bpymenu_set_tooltip(scriptMenu, middle);
842                                                         }
843                                                         parser_state = 0;
844                                                 }
845                                         }
846                                         break;
847
848                                 default:
849                                         parser_state = 0;
850                                         break;
851                         }
852                 }
853         }
854
855         else { /* shouldn't happen, it's checked in bpymenus_ParseDir */
856                 if (DEBUG)
857                         fprintf(stderr, "BPyMenus error: Couldn't open %s.\n", fname);
858                 return -1;
859         }
860
861         return 0;
862 }
863
864 /* bpymenu_ParseDir:
865  * recursively scans folders looking for scripts to register.
866  *
867  * This function scans the scripts directory looking for .py files with the
868  * right header and menu info.
869  * - is_userdir defines if the script is in the default scripts dir or the
870  * user defined one (U.pythondir: is_userdir == 1);
871  * - parentdir is the parent dir name to store as part of the script filename,
872  * if we're down a subdir.
873  * Speed is important.
874  */
875 static int bpymenu_ParseDir(char *dirname, char *parentdir, int is_userdir )
876 {
877         DIR *dir; 
878         FILE *file = NULL;
879         struct dirent *de;
880         struct stat status;
881         char *file_extension;
882         char path[FILE_MAX];
883         char subdir[FILE_MAX];
884         char *s = NULL;
885         
886         dir = opendir(dirname);
887
888         if (dir != NULL) {
889                 while ((de = readdir(dir)) != NULL) {
890
891                         /* skip files and dirs starting with '.' or 'bpy' */
892                         if ((de->d_name[0] == '.') || !strncmp(de->d_name, "bpy", 3)) {
893                                 continue;
894                         }
895                         
896                         BLI_make_file_string("/", path, dirname, de->d_name);
897                         
898                         if (stat(path, &status) != 0) {
899                                 if (DEBUG)
900                                         fprintf(stderr, "stat %s failed: %s\n", path, strerror(errno));
901                         }
902
903                         if (S_ISREG(status.st_mode)) { /* is file */
904
905                                 file_extension = strstr(de->d_name, ".py");
906
907                                 if (file_extension && *(file_extension + 3) == '\0') {
908                                         file = fopen(path, "rb");
909
910                                         if (file) {
911                                                 s = de->d_name;
912                                                 if (parentdir) {
913                                                         /* Join parentdir and de->d_name */
914                                                         BLI_join_dirfile(subdir, parentdir, de->d_name);
915
916                                                         s = subdir;
917                                                 }
918                                                 bpymenu_ParseFile(file, s, is_userdir);
919                                                 fclose(file);
920                                         }
921
922                                         else {
923                                                 if (DEBUG)
924                                                         fprintf(stderr, "BPyMenus error: Couldn't open %s.\n", path);
925                                         }
926                                 }
927                         }
928
929                         else if (S_ISDIR(status.st_mode)) { /* is subdir */
930                                 Dirs_Number++;
931                                 Dir_Depth++;
932                                 if (Dirs_Number > MAX_DIR_NUMBER) {
933                                         if (DEBUG) {
934                                                 fprintf(stderr, "BPyMenus error: too many subdirs.\n");
935                                         }
936                                         closedir(dir);
937                                         return -1;
938                                 }
939                                 else if (Dir_Depth > MAX_DIR_DEPTH) {
940                                         if (DEBUG)
941                                                 fprintf(stderr,
942                                                         "BPyMenus error: max depth reached traversing dir tree.\n");
943                                         closedir(dir);
944                                         return -1;
945                                 }
946                                 s = de->d_name;
947                                 if (parentdir) {
948                                         /* Join parentdir and de->d_name */
949                                         BLI_join_dirfile(subdir, parentdir, de->d_name);                                        
950                                         s = subdir;
951                                 }
952                                 if (bpymenu_ParseDir(path, s, is_userdir) == -1) {
953                                         closedir(dir);
954                                         return -1;
955                                 }
956                                 Dir_Depth--;
957                         }
958
959                 }
960                 closedir(dir);
961         }
962
963         else { /* open directory stream failed */
964                 if (DEBUG)
965                         fprintf(stderr, "opendir %s failed: %s\n", dirname, strerror(errno));
966                 return -1;
967         }
968
969         return 0;
970 }
971
972 static int bpymenu_GetStatMTime( const char *name, int is_file, time_t * mtime )
973 {
974         struct stat st;
975         int result;
976
977 #ifdef WIN32
978         if (is_file) {
979         result = stat( name, &st );
980         } else {
981                 /* needed for win32 only, remove trailing slash */
982                 char name_stat[FILE_MAX];
983                 BLI_strncpy(name_stat, name, FILE_MAX);
984                 BLI_del_slash(name_stat);
985                 result = stat( name_stat, &st );
986         }
987 #else
988         result = stat( name, &st );
989 #endif
990         
991         if( result == -1 )
992                 return -1;
993
994         if( is_file ) {
995                 if( !S_ISREG( st.st_mode ) )
996                         return -2;
997         } else if( !S_ISDIR( st.st_mode ) )
998                 return -2;
999
1000         *mtime = st.st_mtime;
1001
1002         return 0;
1003 }
1004
1005 /* BPyMenu_Init:
1006  * import the bpython menus data to Blender, either from:
1007  * - the BPYMENU_DATAFILE file (?/.blender/Bpymenus) or
1008  * - the scripts dir(s), case newer than the datafile (then update the file).
1009  * then fill the bpymenu table with this data.
1010  * if param usedir != 0, then the data is recreated from the dir(s) anyway.
1011 */
1012 int BPyMenu_Init( int usedir )
1013 {
1014         char fname[FILE_MAXDIR];
1015         char dirname[FILE_MAX];
1016         char upythondir[FILE_MAX];
1017         char *upydir = U.pythondir, *sdir = NULL;
1018         time_t time_dir1 = 0, time_dir2 = 0, time_file = 0;
1019         int stat_dir1 = 0, stat_dir2 = 0, stat_file = 0;
1020         int i;
1021
1022         DEBUG = G.f & G_DEBUG;  /* is Blender in debug mode (started with -d) ? */
1023
1024         /* init global bpymenu table (it is a list of pointers to struct BPyMenus
1025          * for each available group: import, export, etc.) */
1026         for( i = 0; i < PYMENU_TOTAL; i++ )
1027                 BPyMenuTable[i] = NULL;
1028
1029         if( DEBUG )
1030                 fprintf(stdout, "\nRegistering scripts in Blender menus ...\n\n" );
1031
1032         if( U.pythondir[0] == '\0') {
1033                 upydir = NULL;
1034         }
1035         else if (strcmp(U.pythondir, "/") == 0 || strcmp(U.pythondir, "//") == 0) {
1036                 /* these are not accepted to prevent possible slight slowdowns on startup;
1037                  * they should not be used as user defined scripts dir, anyway, also from
1038                  * speed considerations, since they'd not be dedicated scripts dirs */
1039                 if (DEBUG) fprintf(stderr,
1040                         "BPyMenus: invalid user defined Python scripts dir: \"/\" or \"//\".\n");
1041                 upydir = NULL;
1042         }
1043         else {
1044                 BLI_strncpy(upythondir, upydir, FILE_MAX);
1045                 BLI_convertstringcode(upythondir, G.sce);
1046         }
1047
1048         sdir = bpy_gethome(1);
1049
1050         if (sdir) {
1051                 BLI_strncpy(dirname, sdir, FILE_MAX);
1052                 stat_dir1 = bpymenu_GetStatMTime( dirname, 0, &time_dir1 );
1053
1054                 if( stat_dir1 < 0 ) {
1055                         time_dir1 = 0;
1056                         if( DEBUG ) {
1057                                 fprintf(stderr,
1058                                         "\nDefault scripts dir: %s:\n%s\n", dirname, strerror(errno));
1059                                 if( upydir )
1060                                         fprintf(stdout,
1061                                                 "Getting scripts menu data from user defined dir: %s.\n",
1062                                                 upythondir );
1063                         }
1064                 }
1065         }
1066         else stat_dir1 = -1;
1067
1068         if( upydir ) {
1069                 stat_dir2 = bpymenu_GetStatMTime( upythondir, 0, &time_dir2 );
1070
1071                 if( stat_dir2 < 0 ) {
1072                         time_dir2 = 0;
1073                         upydir = NULL;
1074                         if( DEBUG )
1075                                 fprintf(stderr, "\nUser defined scripts dir: %s:\n%s.\n",
1076                                         upythondir, strerror( errno ) );
1077                         if( stat_dir1 < 0 ) {
1078                                 if( DEBUG )
1079                                         fprintf(stderr, "\
1080 To have scripts in menus, please add them to the default scripts dir:\n\
1081 %s\n\
1082 and / or go to 'Info window -> File Paths tab' and set a valid path for\n\
1083 the user defined Python scripts dir.\n", dirname );
1084                                 return -1;
1085                         }
1086                 }
1087         }
1088         else stat_dir2 = -1;
1089
1090         if( ( stat_dir1 < 0 ) && ( stat_dir2 < 0 ) ) {
1091                 if( DEBUG ) {
1092                         fprintf(stderr, "\nCannot register scripts in menus, no scripts dir"
1093                                                         " available.\nExpected default dir at: %s \n", dirname );
1094                 }
1095                 return -1;
1096         }
1097
1098         if (usedir) stat_file = -1;
1099         else { /* if we're not forced to use the dir */
1100                 char *homedir = bpy_gethome(0);
1101
1102                 if (homedir) {
1103                         BLI_make_file_string( "/", fname, homedir, BPYMENU_DATAFILE );
1104                         stat_file = bpymenu_GetStatMTime( fname, 1, &time_file );
1105                         if( stat_file < 0 )
1106                                 time_file = 0;
1107
1108                 /* comparing dates */
1109
1110                         if((stat_file == 0)
1111                                 && (time_file > time_dir1) && (time_file > time_dir2))
1112                         {       /* file is newer */
1113                                 stat_file = bpymenu_CreateFromFile(  ); /* -1 if an error occurred */
1114                                 if( !stat_file && DEBUG )
1115                                         fprintf(stdout,
1116                                                 "Getting menu data for scripts from file:\n%s\n\n", fname );
1117                         }
1118                         else stat_file = -1;
1119                 }
1120                 else stat_file = -1;    /* -1 to use dirs: didn't use file or it was corrupted */
1121         }
1122
1123         if( stat_file == -1 ) { /* use dirs */
1124                 if( DEBUG ) {
1125                         fprintf(stdout,
1126                                 "Getting menu data for scripts from dir(s):\ndefault: %s\n", dirname );
1127                         if( upydir )
1128                                 fprintf(stdout, "user defined: %s\n", upythondir );
1129                         fprintf(stdout, "\n");
1130                 }
1131                 if( stat_dir1 == 0 ) {
1132                         i = bpymenu_ParseDir( dirname, NULL, 0 );
1133                         if (i == -1 && DEBUG)
1134                                 fprintf(stderr, "Default scripts dir does not seem valid.\n\n");
1135                 }
1136                 if( stat_dir2 == 0 ) {
1137                         BLI_strncpy(dirname, U.pythondir, FILE_MAX);
1138                         BLI_convertstringcode(dirname, G.sce);
1139                         i = bpymenu_ParseDir( dirname, NULL, 1 );
1140                         if (i == -1 && DEBUG)
1141                                 fprintf(stderr, "User defined scripts dir does not seem valid.\n\n");
1142                 }
1143
1144                 /* check if we got any data */
1145                 for( i = 0; i < PYMENU_TOTAL; i++ )
1146                         if( BPyMenuTable[i] )
1147                                 break;
1148
1149                 /* if we got, recreate the file */
1150                 if( i < PYMENU_TOTAL )
1151                         bpymenu_WriteDataFile(  );
1152                 else if( DEBUG ) {
1153                         fprintf(stderr, "\n\
1154 Warning: Registering scripts in menus -- no info found.\n\
1155 Either your scripts dirs have no .py scripts or the scripts\n\
1156 don't have a header with registration data.\n\
1157 Default scripts dir is:\n\
1158 %s\n", dirname );
1159                         if( upydir )
1160                                 fprintf(stderr, "User defined scripts dir is: %s\n",
1161                                         upythondir );
1162                 }
1163         }
1164
1165         return 0;
1166 }