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