Tuesday, August 17, 2010

Fun with detailed files

Earlier today, the following question was posted on the use-revolution mailing list: file owner & group names. Rev veteran Monte Goulding was wondering how to convert file owner and group ids into actual user and group names. I happily logged into the Solaris server at work and dug around a bit, finally replying that he could parse this information from the following two files:
/etc/group
/etc/passwd

And what would such a parsing script look like in revTalk? I'm glad you asked:
command LoadGroupIdMap @pGroupIdMap
p_LoadFileIdMap "/etc/group", pGroupIdMap
end LoadGroupIdMap

command LoadUserIdMap @pUserIdMap
p_LoadFileIdMap "/etc/passwd", pUserIdMap
end LoadUserIdMap

private command p_LoadFileIdMap pFile, @pIdMap
local tData, tLine
put URL ("file:" & pFile) into tData
set the itemDelimiter to colon
repeat for each line tLine in tData
-- skip empty and comment lines
if tLine is empty \
or char 1 of tLine is "#" then next repeat
-- fill the id map
put item 1 of tLine into pIdMap[item 3 of tLine]
end repeat
end p_LoadFileIdMap

Given that both files use a colon as item delimiter, and have the name as first item and the id as third item, I extracted the parsing into a separate private command. Put the above handlers in your stack script, and then fetch each id-to-name-mapping before interpreting the detailed files.
Here's a simple button script that uses the above commands:
on mouseUp
local tOldFolder, tNewFolder, tDetailedFiles
answer folder "Select a folder"
if the result is "Cancel" then exit mouseUp
put it into tNewFolder
--> extract the detailed files information
put the defaultFolder into tOldFolder
set the defaultFolder to tNewFolder
put the detailed files into tDetailedFiles
set the defaultFolder to tOldFolder
--> load the group and user id maps
local tGroupIdMap, tUserIdMap
LoadGroupIdMap tGroupIdMap
LoadUserIdMap tUserIdMap
--> build the extended file information
local tDetailedFile, tExtendedFiles
repeat for each line tDetailedFile in tDetailedFiles
-- skip hidden files
if char 1 of tDetailedFile is "." then next repeat
-- fill the extended file entry
put urlDecode(item 1 of tDetailedFile) & tab & \
tUserIdMap[item 8 of tDetailedFile] & tab & \
tGroupIdMap[item 9 of tDetailedFile] & return \
after tExtendedFiles
end repeat
--> wrap things up
put char 1 to -2 of tExtendedFiles into field "Files"
end mouseUp

Once I got home, I wanted to verify this on my MacOS X machine, only to discover that it wasn't properly mapping the uid and gid to user and group names. Intrigued, I dug a bit deeper and found out that MacOS X actually relies on a DirectoryService when it's not running in single-user mode. Lovely, but how can we get what we need using revTalk?
Well, it turns out you can use the nireport command via the shell function to fetch a list of the group and user names with id. So let's add another helper command to do things the MacOS X way, and update our existing commands to use the correct approach, depending on the platform.
command LoadGroupIdMap @pGroupIdMap
if the platform is "MacOS" then
p_LoadNireportIdMap "/groups gid name", pGroupIdMap
else if the platform is "Linux" then
p_LoadFileIdMap "/etc/group", pGroupIdMap
end if
end LoadGroupIdMap

command LoadUserIdMap @pUserIdMap
if the platform is "MacOS" then
p_LoadNireportIdMap "/users uid name", pUserIdMap
else if the platform is "Linux" then
p_LoadFileIdMap "/etc/passwd", pUserIdMap
end if
end LoadUserIdMap

private command p_LoadFileIdMap pFile, @pIdMap
local tData, tLine
put URL ("file:" & pFile) into tData
set the itemDelimiter to colon
repeat for each line tLine in tData
-- skip empty and comment lines
if tLine is empty \
or char 1 of tLine is "#" then next repeat
-- fill the id map
put item 1 of tLine into pIdMap[item 3 of tLine]
end repeat
end p_LoadFileIdMap

private command p_LoadNireportIdMap pParams, @pIdMap
local tData, tLine
put shell ("nireport ." && pParams) into tData
set the itemDelimiter to tab
repeat for each line tLine in tData
put item 2 of tLine into pIdMap[item 1 of tLine]
end repeat
end p_LoadNireportIdMap

This seemed to do the trick on my iMac PowerPC G5 running MacOS X 10.4 Tiger - but when I tried it on my MacBook Pro Intel running MacOS X 10.6 Snow Leopard, it failed as there is no "nireport" command available. As it turns out, Apple dropped NetInfo when MacOS X 10.5 Leopard came out. So my solution is not quite complete, unfortunately.

To be continued...

No comments: