3 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version. The Blender
9 * Foundation also sells licenses for use in proprietary software under
10 * the Blender License. See http://www.blender.org/BL/ for information
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23 * All rights reserved.
25 * This is a new part of Blender.
27 * Contributor(s): Willian P. Germano
29 * ***** END GPL/BL DUAL LICENSE BLOCK *****
32 /* This is the main file responsible for having bpython scripts accessible
33 * from Blender menus. To know more, please start with its header file.
43 #include <sys/types.h>
50 #include "BLI_winstuff.h"
55 #include "BKE_global.h"
56 #include "BKE_utildefines.h"
57 #include "BLI_blenlib.h"
58 #include "MEM_guardedalloc.h"
60 #include <DNA_userdef_types.h> /* for U.pythondir */
62 #include "BPY_extern.h"
63 #include "BPY_menus.h"
67 #define BPYMENU_DATAFILE "Bpymenus"
71 /* BPyMenuTable holds all registered pymenus, as linked lists for each menu
72 * where they can appear (see PYMENUHOOKS enum in BPY_menus.h).
74 BPyMenu *BPyMenuTable[PYMENU_TOTAL];
76 /* we can't be sure if BLI_gethome() returned a path
77 * with '.blender' appended or not. Besides, this function now
78 * either returns userhome/.blender (if it exists) or
79 * blenderInstallDir/.blender/ otherwise */
80 char *bpymenu_gethome()
82 static char homedir[FILE_MAXDIR];
83 char bprogdir[FILE_MAXDIR];
87 if (homedir[0] != '\0') return homedir; /* no need to search twice */
91 if (strstr(s, ".blender")) PyOS_snprintf(homedir, FILE_MAXDIR, s);
92 else BLI_make_file_string ("/", homedir, s, ".blender/");
94 /* if userhome/.blender/ exists, return it */
95 if (BLI_exists(homedir)) return homedir;
97 /* otherwise, use argv[0] (bprogname) to get .blender/ in
98 * Blender's installation dir */
99 s = BLI_last_slash(bprogname);
101 i = s - bprogname + 1;
103 PyOS_snprintf(bprogdir, i, bprogname);
104 BLI_make_file_string ("/", homedir, bprogdir, ".blender/");
109 static int bpymenu_group_atoi (char *str)
111 if (!strcmp(str, "Import")) return PYMENU_IMPORT;
112 else if (!strcmp(str, "Export")) return PYMENU_EXPORT;
113 /* "Misc" or an inexistent group name: use misc */
114 else return PYMENU_MISC;
117 char *BPyMenu_group_itoa (short menugroup)
133 /* BPyMenu_CreatePupmenuStr:
134 * build and return a meaninful string to be used by pupmenu(). The
135 * string is made of a bpymenu name as title and its submenus as possible
136 * choices for the user.
138 char *BPyMenu_CreatePupmenuStr(BPyMenu *pym, short menugroup)
140 BPySubMenu *pysm = pym->submenus;
141 char str[1024], str2[100];
144 if (!pym || !pysm) return NULL;
148 PyOS_snprintf(str2, sizeof(str2), "%s: %s%%t",
149 BPyMenu_group_itoa(menugroup), pym->name);
153 PyOS_snprintf(str2, sizeof(str2), "|%s%%x%d", pysm->name, i);
154 rlen = sizeof(str) - strlen(str);
155 strncat(str, str2, rlen);
160 return BLI_strdup(str);
163 static void bpymenu_RemoveAllSubEntries (BPySubMenu *smenu)
169 if (smenu->name) MEM_freeN(smenu->name);
170 if (smenu->arg) MEM_freeN(smenu->arg);
177 void BPyMenu_RemoveAllEntries (void)
179 BPyMenu *tmp, *pymenu;
182 for (i = 0; i < PYMENU_TOTAL; i++) {
183 pymenu = BPyMenuTable[i];
186 if (pymenu->name) MEM_freeN(pymenu->name);
187 if (pymenu->filename) MEM_freeN(pymenu->filename);
188 if (pymenu->tooltip) MEM_freeN(pymenu->tooltip);
189 if (pymenu->submenus) bpymenu_RemoveAllSubEntries(pymenu->submenus);
193 BPyMenuTable[i] = NULL;
198 static BPyMenu *bpymenu_FindEntry (short group, char *name)
202 if ((group <0) || (group >= PYMENU_TOTAL)) return NULL;
204 pymenu = BPyMenuTable[group];
207 if (!strcmp(pymenu->name, name)) return pymenu;
208 pymenu = pymenu->next;
215 * given a group and a position, return the entry in that position from
218 BPyMenu *BPyMenu_GetEntry (short group, short pos)
222 if ((group < 0) || (group >= PYMENU_TOTAL)) return NULL;
224 pym = BPyMenuTable[group];
227 if (pym) pym = pym->next;
231 return pym; /* found entry or NULL */
234 static void bpymenu_set_tooltip (BPyMenu *pymenu, char *tip)
238 if (pymenu->tooltip) MEM_freeN(pymenu->tooltip);
239 pymenu->tooltip = BLI_strdup(tip);
245 * try to find an existing pymenu entry with the given type and name;
246 * if found, update it with new info, otherwise create a new one and fill it.
248 static BPyMenu *bpymenu_AddEntry (short group, short version, char *name,
249 char *fname, int whichdir, char *tooltip)
251 BPyMenu *menu, *next = NULL, **iter;
254 if ((group < 0) || (group >= PYMENU_TOTAL)) return NULL;
255 if (!name || !fname) return NULL;
257 menu = bpymenu_FindEntry (group, name); /* already exists? */
259 /* if a menu with this name already exists in the same group:
260 * - if one script is in the default dir and the other in U.pythondir,
261 * accept and let the new one override the other.
262 * - otherwise, report the error and return NULL. */
264 if (menu->dir < whichdir) { /* new one is in U.pythondir */
266 if (menu->name) MEM_freeN(menu->name);
267 if (menu->filename) MEM_freeN(menu->filename);
268 if (menu->tooltip) MEM_freeN(menu->tooltip);
269 if (menu->submenus) bpymenu_RemoveAllSubEntries(menu->submenus);
272 else { /* they are in the same dir */
274 printf("\nWarning: script %s's menu name is already in use.\n", fname);
275 printf ("Edit the script and change its Name: '%s' field, please.\n"
276 "Note: if you really want two scripts in the same menu with\n"
277 "the same name, keep one in the default dir and the other in\n"
278 "the user defined dir, where it will take precedence.\n", name);
283 else menu = MEM_mallocN(sizeof(BPyMenu), "pymenu");
285 if (!menu) return NULL;
287 menu->name = BLI_strdup(name);
288 menu->version = version;
289 menu->filename = BLI_strdup(fname);
290 menu->tooltip = NULL;
291 if (tooltip) menu->tooltip = BLI_strdup(tooltip);
292 menu->dir = whichdir;
293 menu->submenus = NULL;
294 menu->next = next; /* non-NULL if menu already existed */
296 if (nameclash) return menu; /* no need to place it, it's already at the list*/
298 iter = &BPyMenuTable[group];
299 while (*iter) iter = &((*iter)->next);
306 /* bpymenu_AddSubEntry:
307 * add a submenu to an existing python menu.
309 static int bpymenu_AddSubEntry (BPyMenu *mentry, char *name, char *arg)
311 BPySubMenu *smenu, **iter;
313 smenu = MEM_mallocN(sizeof(BPySubMenu), "pysubmenu");
314 if (!smenu) return -1;
316 smenu->name = BLI_strdup(name);
317 smenu->arg = BLI_strdup(arg);
320 if (!smenu->name || !smenu->arg) return -1;
322 iter = &(mentry->submenus);
323 while (*iter) iter = &((*iter)->next);
330 /* bpymenu_CreateFromFile:
331 * parse the bpymenus data file where Python menu data is stored;
332 * based on this data, create and fill the pymenu structs.
334 static int bpymenu_CreateFromFile (void)
337 char line[255], w1[255], w2[255], tooltip[255], *tip;
338 int parsing, version, whichdir;
340 BPyMenu *pymenu = NULL;
342 /* init global bpymenu table (it is a list of pointers to struct BPyMenus
343 * for each available cathegory: import, export, etc.) */
344 for (group = 0; group < PYMENU_TOTAL; group++)
345 BPyMenuTable[group] = NULL;
347 /* let's try to open the file with bpymenu data */
348 BLI_make_file_string ("/", line, bpymenu_gethome(), BPYMENU_DATAFILE);
350 fp = fopen(line, "rb");
353 if (DEBUG) printf("BPyMenus error: couldn't open config file %s.\n", line);
357 fgets(line, 255, fp); /* header */
359 /* check if the U.pythondir we saved at the file is different from the
360 * current one. If so, return to force updating from dirs */
362 fscanf(fp, "# User defined scripts dir: %[^\n]\n", w1);
364 if (strcmp(w1, U.pythondir) != 0) return -1;
368 while (fgets(line, 255, fp)) { /* parsing file lines */
370 switch (line[0]) { /* check first char */
371 case '#': /* comment */
378 parsing = sscanf(line, "%s {\n", w1); /* menu group */
382 if (parsing == 1) { /* got menu group string */
383 group = bpymenu_group_atoi(w1);
384 if (group < 0 && DEBUG) { /* invalid type */
385 printf("BPyMenus error parsing config file: wrong group: %s, "
386 "will use 'Misc'.\n", w1);
392 tip = NULL; /* optional tooltip */
393 fgets(line, 255, fp);
394 if (line[0] == '}') break;
395 else if (line[0] == '\n') continue;
396 else if (line[0] == '\'') { /* menu entry */
397 parsing = sscanf(line, "'%[^']' %d %s %d '%[^']'\n", w1, &version, w2, &whichdir, tooltip);
399 if (parsing <= 0) { /* invalid line, get rid of it */
400 fgets(line, 255, fp);
402 else if (parsing == 5) tip = tooltip; /* has tooltip */
404 pymenu = bpymenu_AddEntry(group, (short)version, w1, w2, whichdir, tip);
406 puts("BPyMenus error: couldn't create bpymenu entry.\n");
411 else if (line[0] == '|' && line[1] == '_') { /* menu sub-entry */
412 if (!pymenu) continue; /* no menu yet, skip this line */
413 sscanf(line, "|_%[^:]: %s\n", w1, w2);
414 bpymenu_AddSubEntry(pymenu, w1, w2);
423 /* bpymenu_WriteDataFile:
424 * writes the registered scripts info to the user's home dir, for faster
425 * access when the scripts dir hasn't changed.
427 static void bpymenu_WriteDataFile(void)
432 char fname[FILE_MAXDIR+FILE_MAXFILE];
435 BLI_make_file_string("/", fname, bpymenu_gethome(), BPYMENU_DATAFILE);
437 fp = fopen(fname, "w");
439 if (DEBUG) printf("BPyMenus error: couldn't write %s file.", fname);
443 fprintf(fp, "# Blender: registered menu entries for bpython scripts\n");
445 if (U.pythondir[0] != '\0')
446 fprintf(fp, "# User defined scripts dir: %s\n", U.pythondir);
448 for (i = 0; i < PYMENU_TOTAL; i++) {
449 pymenu = BPyMenuTable[i];
450 if (!pymenu) continue;
451 fprintf(fp, "\n%s {\n", BPyMenu_group_itoa(i));
453 fprintf(fp,"'%s' %d %s %d", pymenu->name, pymenu->version, pymenu->filename, pymenu->dir);
454 if (pymenu->tooltip) fprintf(fp, " '%s'\n", pymenu->tooltip);
455 else fprintf(fp, "\n");
456 smenu = pymenu->submenus;
458 fprintf(fp, "|_%s: %s\n", smenu->name, smenu->arg);
461 pymenu = pymenu->next;
470 /* BPyMenu_PrintAllEntries:
471 * useful for debugging.
473 void BPyMenu_PrintAllEntries(void)
479 printf("# Blender: registered menu entries for bpython scripts\n");
481 for (i = 0; i < PYMENU_TOTAL; i++) {
482 pymenu = BPyMenuTable[i];
483 printf("\n%s {\n", BPyMenu_group_itoa(i));
485 printf("'%s' %d %s %d", pymenu->name, pymenu->version, pymenu->filename, pymenu->dir);
486 if (pymenu->tooltip) printf(" '%s'\n", pymenu->tooltip);
488 smenu = pymenu->submenus;
490 printf("|_%s: %s\n", smenu->name, smenu->arg);
493 pymenu = pymenu->next;
499 /* bpymenu_GetDataFromDir:
500 * this function scans the scripts dir looking for .py files with the
501 * right header and menu info, using that to fill the bpymenu structs.
502 * whichdir defines if the script is in the default scripts dir or the
503 * user defined one (U.pythondir: whichdir == 1).
504 * Speed is important.
506 static int bpymenu_CreateFromDir (char *dirname, int whichdir)
511 struct dirent *dir_entry;
513 char *s, *fname, str[FILE_MAXFILE+FILE_MAXDIR];
514 char line[100], w[100];
515 char name[100], submenu[100], subarg[100], tooltip[100];
516 int res = 0, version = 0;
518 dir = opendir(dirname);
522 /* we scan the dir for filenames ending with .py and starting with the
523 * right 'magic number': '#!BPY'. All others are ignored. */
525 while ((dir_entry = readdir(dir)) != NULL) {
526 fname = dir_entry->d_name;
527 /* ignore anything starting with a dot */
528 if (fname[0] == '.') continue; /* like . and .. */
530 /* also skip filenames whose extension isn't '.py' */
531 s = strstr(fname, ".py");
532 if (!s || *(s+3) != '\0') continue;
534 BLI_make_file_string("/", str, dirname, fname);
536 /* paranoia: check if this is really a file and not a disguised dir */
537 if ((stat(str, &st) == -1) || !S_ISREG(st.st_mode)) continue;
539 fp = fopen(str, "rb");
542 if (DEBUG) printf("BPyMenus error: couldn't open %s.\n", str);
546 /* finally, look for the start string '#!BPY', with
547 * or w/o white space(s) between #! and BPY */
548 fgets(line, 100, fp);
549 if (line[0] != '#' || line[1] != '!') goto discard;
551 if (!strstr (line, "BPY")) goto discard;
553 /* file passed the tests, look for the three double-quotes */
554 while (fgets(line, 100, fp)) {
555 if (line[0] == '"' && line[1] == '"' && line[2] == '"') {
561 if (!res) goto discard;
563 /* Now we're ready to get the registration info. A little more structure
564 * was imposed to their format, for speed. The registration
565 * lines must appear between the first pair of triple double-quotes and
566 * follow this order (the single-quotes are part of the format):
568 * Name: 'script name for the menu'
569 * Group: 'group name' (defines menu)
570 * Blender: <short int> (minimal Blender version)
571 * Submenu: 'submenu name' related_1word_arg
572 * Tooltip: 'tooltip for the menu'
575 * - there may be more than one submenu line, or none:
576 * submenus and the tooltip are optional;
577 * - the Blender version is the same number reported by
578 * Blender.Get('version') in BPython or G.version in C;
579 * - only the first letter of each token is checked, both lower
580 * and upper cases, so that's all that matters for recognition:
581 * n 'script name' is enough for the name line, for example. */
583 /* first the name: */
584 res = fscanf(fp, "%[^']'%[^'\r\n]'\n", w, name);
585 if ((res != 2) || (w[0] != 'n' && w[0] != 'N')) {
586 if (DEBUG) printf("BPyMenus error: wrong 'name' line in %s.\n", str);
590 line[0] = '\0'; /* used as group for this part */
592 /* minimal Blender version: */
593 res = fscanf(fp, "%s %d\n", w, &version);
594 if ((res != 2) || (w[0] != 'b' && w[0] != 'B')) {
595 if (DEBUG) printf("BPyMenus error: wrong 'blender' line in %s.\n", str);
600 res = fscanf(fp, "%[^']'%[^'\r\n]'\n", w, line);
601 if ((res != 2) || (w[0] != 'g' && w[0] != 'G')) {
602 if (DEBUG) printf("BPyMenus error: wrong 'group' line in %s.\n", str);
606 res = bpymenu_group_atoi(line);
608 if (DEBUG) printf("BPyMenus error: unknown 'group' %s in %s.\n", line, str);
612 pymenu = bpymenu_AddEntry(res, (short)version, name, fname, whichdir, NULL);
614 if (DEBUG) printf("BPyMenus error: couldn't create entry for %s.\n", str);
620 /* the (optional) submenu(s): */
621 while (fgets (line, 100, fp)) {
622 res = sscanf(line, "%[^']'%[^'\r\n]'%s\n", w, submenu, subarg);
623 if ((res != 3) || (w[0] != 's' && w[0] != 'S')) break;
624 bpymenu_AddSubEntry(pymenu, submenu, subarg);
627 /* the (optional) tooltip: */
628 res = sscanf(line, "%[^']'%[^'\r\n]'\n", w, tooltip);
629 if ((res == 2) && (w[0] == 't' || w[0] == 'T')) {
630 bpymenu_set_tooltip (pymenu, tooltip);
642 static int bpymenu_GetStatMTime(char *name, int is_file, time_t* mtime)
647 result = stat(name, &st);
649 if (result == -1) return -1;
651 if (is_file) { if (!S_ISREG(st.st_mode)) return -2; }
652 else if (!S_ISDIR(st.st_mode)) return -2;
654 *mtime = st.st_mtime;
660 * import the bpython menus data to Blender, either from:
661 * - the BPYMENU_DATAFILE file (?/.blender/Bpymenus) or
662 * - the scripts dir(s), case newer than the datafile (then update the file).
663 * then fill the bpymenu table with this data.
664 * if param usedir != 0, then the data is recreated from the dir(s) anyway.
666 int BPyMenu_Init(int usedir)
668 char fname[FILE_MAXDIR+FILE_MAXFILE];
669 char dirname[FILE_MAXDIR];
670 char *upydir = U.pythondir;
671 time_t tdir1, tdir2, tfile;
672 int res1, res2, resf = 0;
674 DEBUG = G.f & G_DEBUG; /* is Blender in debug mode (started with -d) ? */
676 /* init global bpymenu table (it is a list of pointers to struct BPyMenus
677 * for each available group: import, export, etc.) */
678 for (res1 = 0; res1 < PYMENU_TOTAL; res1++)
679 BPyMenuTable[res1] = NULL;
681 if (U.pythondir[0] == '\0') upydir = NULL;
683 BLI_make_file_string ("/", dirname, bpymenu_gethome(), "scripts/");
685 res1 = bpymenu_GetStatMTime(dirname, 0, &tdir1);
690 printf ("\nDefault scripts dir: %s:\n%s\n", dirname, strerror(errno));
692 printf("Getting scripts menu data from user defined dir: %s.\n",upydir);
695 else { syspath_append(dirname); }
698 res2 = bpymenu_GetStatMTime(U.pythondir, 0, &tdir2);
702 if (DEBUG) printf("\nUser defined scripts dir: %s:\n%s.\n", upydir, strerror(errno));
704 if (DEBUG) printf ("To have scripts in menus, please add them to the"
705 "default scripts dir: %s\n"
706 "and/or go to 'Info window -> File Paths tab' and set a valid\n"
707 "path for the user defined scripts dir.\n", dirname);
714 if ((res1 < 0) && (res2 < 0)) {
716 printf ("\nCannot register scripts in menus, no scripts dir"
717 " available.\nExpected default dir in %s .\n", dirname);
722 if (DEBUG) printf("\nRegistering scripts in Blender menus ...\n\n");
724 if (!usedir) { /* if we're not forced to use the dir */
725 BLI_make_file_string("/", fname, bpymenu_gethome(), BPYMENU_DATAFILE);
726 resf = bpymenu_GetStatMTime(fname, 1, &tfile);
727 if (resf < 0) tfile = 0;
730 /* comparing dates */
732 if ((tfile > tdir1) && (tfile > tdir2) && !resf) { /* file is newer */
733 resf = bpymenu_CreateFromFile(); /* -1 if an error occurred */
735 printf("Getting menu data for scripts from file: %s\n\n", fname);
737 else resf = -1; /* -1 to use dirs: didn't use file or it was corrupted */
739 if (resf == -1) { /* use dirs */
741 printf("Getting menu data for scripts from dir(s):\n%s\n", dirname);
742 if (upydir) printf("%s\n", upydir);
744 if (res1 == 0) bpymenu_CreateFromDir(dirname, 0);
745 if (res2 == 0) bpymenu_CreateFromDir(U.pythondir, 1);
747 /* check if we got any data */
748 for (res1 = 0; res1 < PYMENU_TOTAL; res1++)
749 if (BPyMenuTable[res1]) break;
751 /* if we got, recreate the file */
752 if (res1 < PYMENU_TOTAL) bpymenu_WriteDataFile();
754 printf ("\nWarning: Registering scripts in menus -- no info found.\n"
755 "Either your scripts dirs have no .py scripts or the scripts\n"
756 "don't have a header with registration data.\n"
757 "Default scripts dir is: %s\n", dirname);
759 printf("User defined scripts dir is: %s\n", upydir);