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